From 288517db7c13426f25b9096e04541dc154609356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20V=C3=A1vra?= Date: Mon, 4 Nov 2024 15:02:03 +0100 Subject: [PATCH] Enable gating for ding-libs by adding tests Resolves: RHELMISC-3929 --- tests/sanity/Makefile | 37 + tests/sanity/collection_queue_ut.c | 212 +++ tests/sanity/collection_stack_ut.c | 129 ++ tests/sanity/collection_ut.c | 2259 ++++++++++++++++++++++++ tests/sanity/dhash_ut_check.c | 224 +++ tests/sanity/ding-libs-tests.sh | 3 + tests/sanity/ini_augment_ut_check.c | 341 ++++ tests/sanity/ini_comment_ut.c | 428 +++++ tests/sanity/ini_configmod_ut_check.c | 1692 ++++++++++++++++++ tests/sanity/ini_parse_ut_check.c | 306 ++++ tests/sanity/ini_validators_ut_check.c | 1108 ++++++++++++ tests/sanity/ini_valueobj_ut.c | 741 ++++++++ tests/sanity/path_utils_ut.c | 768 ++++++++ tests/sanity/ref_array_ut.c | 775 ++++++++ tests/sanity/simplebuffer_ut.c | 145 ++ tests/sanity/trace.h | 175 ++ tests/tests.yml | 28 + 17 files changed, 9371 insertions(+) create mode 100644 tests/sanity/Makefile create mode 100644 tests/sanity/collection_queue_ut.c create mode 100644 tests/sanity/collection_stack_ut.c create mode 100644 tests/sanity/collection_ut.c create mode 100644 tests/sanity/dhash_ut_check.c create mode 100755 tests/sanity/ding-libs-tests.sh create mode 100644 tests/sanity/ini_augment_ut_check.c create mode 100644 tests/sanity/ini_comment_ut.c create mode 100644 tests/sanity/ini_configmod_ut_check.c create mode 100644 tests/sanity/ini_parse_ut_check.c create mode 100644 tests/sanity/ini_validators_ut_check.c create mode 100644 tests/sanity/ini_valueobj_ut.c create mode 100644 tests/sanity/path_utils_ut.c create mode 100644 tests/sanity/ref_array_ut.c create mode 100644 tests/sanity/simplebuffer_ut.c create mode 100644 tests/sanity/trace.h create mode 100644 tests/tests.yml diff --git a/tests/sanity/Makefile b/tests/sanity/Makefile new file mode 100644 index 0000000..639d85a --- /dev/null +++ b/tests/sanity/Makefile @@ -0,0 +1,37 @@ +tests: collection_ut.c collection_queue_ut.c collection_stack_ut.c \ + dhash_ut_check.c ini_augment_ut_check.c ini_comment_ut.c \ + ini_configmod_ut_check.c ini_parse_ut_check.c \ + ini_validators_ut_check.c ini_valueobj_ut.c path_utils_ut.c \ + ref_array_ut.c simplebuffer_ut.c + gcc collection_ut.c -lcollection -o collection_ut + gcc collection_queue_ut.c -lcollection -o collection_queue_ut + gcc collection_stack_ut.c -lcollection -o collection_stack_ut + gcc dhash_ut_check.c -ldhash -lcheck -o dhash_ut_check + gcc ini_augment_ut_check.c -lini_config -lcollection -lcheck -lref_array \ + -o ini_augment_ut_check + gcc ini_comment_ut.c -lini_config -lcheck -o ini_comment_ut + gcc ini_configmod_ut_check.c -lbasicobjects -lcheck -lini_config \ + -o ini_configmod_ut_check + gcc ini_parse_ut_check.c -lcheck -lini_config -o ini_parse_ut_check + gcc ini_validators_ut_check.c -lini_config -lcheck \ + -o ini_validators_ut_check + gcc ini_valueobj_ut.c -lini_config -lbasicobjects -o ini_valueobj_ut + gcc path_utils_ut.c -lpath_utils -lcheck -o path_utils_ut + gcc ref_array_ut.c -lref_array -o ref_array_ut + gcc simplebuffer_ut.c -lbasicobjects -o simplebuffer_ut + +run: tests + ./collection_ut || exit 1 + ./collection_queue_ut || exit 2 + ./collection_stack_ut || exit 3 + ./dhash_ut_check || exit 4 + ./ini_augment_ut_check || exit 5 + ./ini_comment_ut || exit 6 + ./ini_configmod_ut_check || exit 7 + ./ini_parse_ut_check || exit 8 + ./ini_validators_ut_check || exit 9 + ./ini_valueobj_ut || exit 10 + ./path_utils_ut || exit 11 + ./ref_array_ut || exit 12 + ./simplebuffer_ut || exit 13 + echo ALL TESTS PASSED diff --git a/tests/sanity/collection_queue_ut.c b/tests/sanity/collection_queue_ut.c new file mode 100644 index 0000000..e5ee86c --- /dev/null +++ b/tests/sanity/collection_queue_ut.c @@ -0,0 +1,212 @@ +/* + QUEUE INTERFACE + + Queue unit test. + + Copyright (C) Dmitri Pal 2009 + + Collection Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Collection Library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Collection Library. If not, see . +*/ + +#include +#include +#define TRACE_HOME +#include "trace.h" +#include "collection_queue.h" +#include "collection_tools.h" + +typedef int (*test_fn)(void); + +int verbose = 0; + +#define COLOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + + +static int queue_test(void) +{ + struct collection_item *queue = NULL; + char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + struct collection_item *item = NULL; + int i; + unsigned count; + int error = EOK; + + TRACE_FLOW_STRING("queue_test","Entry."); + + COLOUT(printf("\n\nQUEUE TEST!!!.\n\n\n")); + + if((error = col_create_queue(&queue)) || + (error = col_enqueue_str_property(queue, "item1","value 1" ,0)) || + (error = col_enqueue_int_property(queue, "item2", -1)) || + (error = col_enqueue_unsigned_property(queue, "item3", 1)) || + (error = col_enqueue_long_property(queue, "item4", 100)) || + (error = col_enqueue_ulong_property(queue, "item5", 1000)) || + (error = col_enqueue_double_property(queue, "item6", 1.1)) || + (error = col_enqueue_bool_property(queue, "item7", 1)) || + (error = col_enqueue_binary_property(queue, "item8", binary_dump, sizeof(binary_dump)))) { + printf("Failed to enqueue property. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + + COLOUT(col_debug_collection(queue,COL_TRAVERSE_DEFAULT)); + + error = col_get_collection_count(queue, &count); + if (error) { + printf("Failed to get count. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + + count--; + + COLOUT(printf("Rotate the queue.\n")); + + for (i = 0; i < count; i++) { + if ((error = col_dequeue_item(queue, &item)) || + (error = col_enqueue_item(queue, item))) { + printf("Failed to dequeue or enqueue items. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + COLOUT(col_debug_collection(queue,COL_TRAVERSE_DEFAULT)); + } + + col_destroy_collection(queue); + TRACE_FLOW_NUMBER("queue_test. Returning", error); + + COLOUT(printf("\n\nEND OF QUEUE TEST!!!.\n\n\n")); + + return error; +} + + +static int empty_test(void) +{ + struct collection_item *queue = NULL; + struct collection_item *item = NULL; + int i; + unsigned count; + int error = EOK; + + TRACE_FLOW_STRING("empty_test","Entry."); + + COLOUT(printf("\n\nEMPTY QUEUE TEST!!!.\n\n\n")); + + if((error = col_create_queue(&queue)) || + (error = col_enqueue_str_property(queue, "item1","value 1" ,0)) || + (error = col_enqueue_int_property(queue, "item2", -1)) || + (error = col_enqueue_unsigned_property(queue, "item3", 1))) { + printf("Failed to enqueue property. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + + COLOUT(col_debug_collection(queue,COL_TRAVERSE_DEFAULT)); + + error = col_get_collection_count(queue, &count); + if (error) { + printf("Failed to get count. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + + count--; + + COLOUT(printf("Empty the queue.\n")); + + for (i = 0; i < count; i++) { + if ((error = col_dequeue_item(queue, &item))) { + printf("Failed to dequeue or enqueue items. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + + col_delete_item(item); + + COLOUT(col_debug_collection(queue,COL_TRAVERSE_DEFAULT)); + } + + COLOUT(printf("Add elements again.\n")); + if((error = col_enqueue_str_property(queue, "item1","value 1" ,0)) || + (error = col_enqueue_int_property(queue, "item2", -1)) || + (error = col_enqueue_unsigned_property(queue, "item3", 1))) { + printf("Failed to enqueue property. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + + COLOUT(col_debug_collection(queue,COL_TRAVERSE_DEFAULT)); + + error = col_get_collection_count(queue, &count); + if (error) { + printf("Failed to get count. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + + count--; + + COLOUT(printf("Empty the queue again.\n")); + + for (i = 0; i < count; i++) { + if ((error = col_dequeue_item(queue, &item))) { + printf("Failed to dequeue or enqueue items. Error %d\n", error); + col_destroy_collection(queue); + return error; + } + + col_delete_item(item); + + COLOUT(col_debug_collection(queue,COL_TRAVERSE_DEFAULT)); + } + + col_destroy_collection(queue); + + TRACE_FLOW_NUMBER("empty_test. Returning", error); + + COLOUT(printf("\n\nEND OF QUEUE TEST!!!.\n\n\n")); + + return error; +} + + +/* Main function of the unit test */ +int main(int argc, char *argv[]) +{ + int error = 0; + test_fn tests[] = { queue_test, + empty_test, + NULL }; + test_fn t; + int i = 0; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + + printf("Start\n"); + + while ((t = tests[i++])) { + error = t(); + if (error) { + printf("Failed!\n"); + return error; + } + } + + printf("Success!\n"); + return 0; +} diff --git a/tests/sanity/collection_stack_ut.c b/tests/sanity/collection_stack_ut.c new file mode 100644 index 0000000..a595e27 --- /dev/null +++ b/tests/sanity/collection_stack_ut.c @@ -0,0 +1,129 @@ +/* + STACK INTERFACE + + Stack unit test. + + Copyright (C) Dmitri Pal 2009 + + Collection Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Collection Library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Collection Library. If not, see . +*/ + +#include +#include +#define TRACE_HOME +#include "trace.h" +#include "collection_stack.h" +#include "collection_tools.h" + +typedef int (*test_fn)(void); + +int verbose = 0; + +#define COLOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + + + +static int stack_test(void) +{ + struct collection_item *stack = NULL; + char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + struct collection_item *item1 = NULL; + struct collection_item *item2 = NULL; + + int error = EOK; + + TRACE_FLOW_STRING("stack_test", "Entry."); + + COLOUT(printf("\n\nSTACK TEST!!!.\n\n\n")); + + if ((error = col_create_stack(&stack)) || + (error = col_push_str_property(stack, "item1", "value 1", 0)) || + (error = col_push_int_property(stack, "item2", -1)) || + (error = col_push_unsigned_property(stack, "item3", 1)) || + (error = col_push_long_property(stack, "item4", 100)) || + (error = col_push_ulong_property(stack, "item5", 1000)) || + (error = col_push_double_property(stack, "item6", 1.1)) || + (error = col_push_bool_property(stack, "item7", 1)) || + (error = col_push_binary_property(stack, "item8", binary_dump, sizeof(binary_dump)))) { + printf("Failed to push property. Error %d\n", error); + col_destroy_collection(stack); + return error; + } + + COLOUT(col_debug_collection(stack, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Swapping last two items by popping and pushing them back.\n")); + + if ((error = col_pop_item(stack, &item1)) || + (error = col_pop_item(stack, &item2))) { + printf("Failed to pop items. Error %d\n", error); + col_destroy_collection(stack); + return error; + } + + COLOUT(printf("\nPopped two last items.\n")); + COLOUT(col_debug_collection(stack, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("\nLast item.\n")); + COLOUT(col_debug_item(item1)); + + COLOUT(printf("\nPrevious item.\n")); + COLOUT(col_debug_item(item2)); + + if ((error = col_push_item(stack, item1)) || + (error = col_push_item(stack, item2))) { + printf("Failed to pop or push items. Error %d\n", error); + col_destroy_collection(stack); + return error; + } + + COLOUT(printf("\n\nPushed two items again in reverse order.\n\n")); + + COLOUT(col_debug_collection(stack, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(stack); + TRACE_FLOW_NUMBER("stack_test. Returning", error); + + COLOUT(printf("\n\nEND OF STACK TEST!!!.\n\n")); + + return error; +} + +/* Main function of the unit test */ + +int main(int argc, char *argv[]) +{ + int error = 0; + test_fn tests[] = { stack_test, + NULL }; + test_fn t; + int i = 0; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + + printf("Start\n"); + + while ((t = tests[i++])) { + error = t(); + if (error) { + printf("Failed!\n"); + return error; + } + } + + printf("Success!\n"); + return 0; +} diff --git a/tests/sanity/collection_ut.c b/tests/sanity/collection_ut.c new file mode 100644 index 0000000..ac246d1 --- /dev/null +++ b/tests/sanity/collection_ut.c @@ -0,0 +1,2259 @@ +/* + COLLECTION LIBRARY + + Collection unit test. + + Copyright (C) Dmitri Pal 2009 + + Collection Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Collection Library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Collection Library. If not, see . +*/ + +#include +#include +#include +#define TRACE_HOME +#include "trace.h" +#include "collection.h" +#include "collection_tools.h" + +typedef int (*test_fn)(void); + +int verbose = 0; + +#define COLOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + +static int dup_test(void) +{ + int error = EOK; + struct collection_item *dup = NULL; + struct collection_item *item = NULL; + int i; + const char *results[] = { "value5", + "value5", + "value0", + "value1", + "value2", + "value3", + "value4", + "value5" }; + + TRACE_FLOW_ENTRY(); + + COLOUT(printf("\n\nDUP TEST!!!.\n\n\n")); + COLOUT(printf("Creating DUP collection.\n")); + + if ((error = col_create_collection(&dup, "dup", 0)) || + (error = col_add_str_property(dup, NULL, "key", "value0", 0)) || + (error = col_add_str_property(dup, NULL, "key", "value1", 0)) || + (error = col_add_str_property(dup, NULL, "key", "value2", 0)) || + (error = col_add_str_property(dup, NULL, "foo", "bar", 0)) || + (error = col_add_str_property(dup, NULL, "key", "value3", 0)) || + (error = col_add_str_property(dup, NULL, "key", "value4", 0)) || + (error = col_add_str_property(dup, NULL, "key", "value5", 0))) { + printf("Failed to add property. Error %d\n", error); + col_destroy_collection(dup); + return error; + } + + COLOUT(col_debug_collection(dup, COL_TRAVERSE_DEFAULT)); + + if (!col_get_dup_item(NULL, NULL, "key", COL_TYPE_ANY, 10000, 0, &item) || + !col_get_dup_item(dup, NULL, NULL, COL_TYPE_ANY, 10000, 0, &item) || + !col_get_dup_item(dup, NULL, "key", COL_TYPE_ANY, 10000, 1, &item) || + !col_get_dup_item(dup, NULL, "key", COL_TYPE_STRING, 0, 1, NULL)) { + /* Expected error but did not get it */ + printf("Expected error but got success.\n"); + col_destroy_collection(dup); + return EINVAL; + } + + for (i = 0; i < 6; i++) { + error = col_get_dup_item(dup, NULL, "key", + COL_TYPE_STRING, i, 1, &item); + if (error) { + printf("Search returned error %d.\n", error); + col_destroy_collection(dup); + return error; + } + + if (!item) { + printf("Item should be found - but in reality is NULL.\n"); + col_destroy_collection(dup); + return EINVAL; + } + + if (strcmp((char *)col_get_item_data(item), results[i+2]) != 0) { + printf("Expected %s got %s.\n", results[i+2], + (char *)col_get_item_data(item)); + col_destroy_collection(dup); + return EINVAL; + } + } + + col_destroy_collection(dup); + + TRACE_FLOW_EXIT(); + + COLOUT(printf("\n\nEND OF DUP TEST!!!.\n\n\n")); + + return EOK; +} + + +static int ref_collection_test(void) +{ + struct collection_item *peer = NULL; + struct collection_item *socket = NULL; + struct collection_item *socket2 = NULL; + char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + + int error = EOK; + + TRACE_FLOW_STRING("ref_collection_test", "Entry."); + + COLOUT(printf("\n\nREF TEST!!!.\n\n\n")); + COLOUT(printf("Creating PEER collection.\n")); + + if ((error = col_create_collection(&peer, "peer", 0)) || + (error = col_add_str_property(peer, NULL, "hostname", "peerhost.mytest.com", 0)) || + /* Expect trailing zero to be truncated */ + (error = col_add_str_property(peer, NULL, "IPv4", "10.10.10.10", 12)) || + (error = col_add_str_property(peer, NULL, "IPv6", "bla:bla:bla:bla:bla:bla", 0))) { + printf("Failed to add property. Error %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("Creating SOCKET collection.\n")); + + if ((error = col_create_collection(&socket, "socket", 0)) || + (error = col_add_int_property(socket, NULL, "id", 1)) || + (error = col_add_long_property(socket, NULL, "packets", 100000000L)) || + (error = col_add_binary_property(socket, NULL, "stack", binary_dump, sizeof(binary_dump)))) { + col_destroy_collection(peer); + col_destroy_collection(socket); + printf("Failed to add property. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket, COL_TRAVERSE_DEFAULT)); + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Adding PEER collection to SOCKET collection as a reference named PEER\n")); + + /* Embed peer host into the socket2 as reference */ + error = col_add_collection_to_collection(socket, NULL, "peer", peer, COL_ADD_MODE_REFERENCE); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(socket); + printf("Failed to add collection to collection. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket, COL_TRAVERSE_DEFAULT)); + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("About to destroy PEER\n")); + col_destroy_collection(peer); + COLOUT(col_debug_collection(socket, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("About to extract PEER\n")); + error = col_get_collection_reference(socket, &peer, "peer"); + if (error) { + col_destroy_collection(socket); + printf("Failed to extract collection. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket, COL_TRAVERSE_DEFAULT)); + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(peer); + + COLOUT(col_debug_collection(socket, COL_TRAVERSE_DEFAULT)); + + error = col_get_collection_reference(socket, &socket2, NULL); + if (error) { + col_destroy_collection(socket); + printf("Failed to extract collection. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket2, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(socket); + COLOUT(col_debug_collection(socket2, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(socket2); + + TRACE_FLOW_NUMBER("ref_collection_test. Returning", error); + + COLOUT(printf("\n\nEND OF REF TEST!!!.\n\n\n")); + + return error; + +} + + +static int single_collection_test(void) +{ + struct collection_item *handle = NULL; + int error = EOK; + + TRACE_FLOW_STRING("single_collection_test", "Entry."); + + if ((error = col_create_collection(&handle, "string_test", 0)) || + (error = col_add_str_property(handle, NULL, "property_1", "some data", 0)) || + (error = col_add_str_property(handle, NULL, "property_2", "some other data", 2)) || + (error = col_add_str_property(handle, NULL, "property_3", "more data", 7))) { + printf("Failed to add property. Error %d\n", error); + col_destroy_collection(handle); + return error; + } + + error = col_add_str_property(handle, NULL, "property 1!", "some data", 0); + if (error) { + COLOUT(printf("Expected error adding bad property to collection %d\n", error)); + } + else { + printf("Expected error but got success\n"); + return -1; + } + + error = col_add_double_property(handle, NULL, "double", 0.253545); + if (error) { + printf("Failed to add double property. Error %d\n", error); + col_destroy_collection(handle); + return error; + } + + error = col_update_double_property(handle, "double", COL_TRAVERSE_DEFAULT, 1.999999); + if (error) { + printf("Failed to update double property. Error %d\n", error); + col_destroy_collection(handle); + return error; + } + + COLOUT(printf("Created collection\n")); + + /* Traverse collection */ + if (verbose) { + error = col_debug_collection(handle, COL_TRAVERSE_DEFAULT); + if (error) { + printf("Error debugging collection %d\n", error); + return error; + } + error = col_print_collection(handle); + if (error) { + printf("Error printing collection %d\n", error); + return error; + } + } + + col_destroy_collection(handle); + + TRACE_FLOW_NUMBER("single_collection_test. Error: ", error); + return error; +} + +static int add_collection_test(void) +{ + struct collection_item *peer = NULL; + struct collection_item *socket = NULL; + char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + + int error = EOK; + + TRACE_FLOW_STRING("add_collection_test", "Entry."); + + COLOUT(printf("\n\nADD TEST!!!.\n\n\n")); + COLOUT(printf("Creating PEER collection.\n")); + + if ((error = col_create_collection(&peer, "peer", 0)) || + (error = col_add_str_property(peer, NULL, "hostname", "peerhost.mytest.com", 0)) || + /* Expect trailing zero to be truncated */ + (error = col_add_str_property(peer, NULL, "IPv4", "10.10.10.10", 12)) || + (error = col_add_str_property(peer, NULL, "IPv6", "bla:bla:bla:bla:bla:bla", 0))) { + printf("Failed to add property. Error %d", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("Creating SOCKET collection.\n")); + + if ((error = col_create_collection(&socket, "socket", 0)) || + (error = col_add_int_property(socket, NULL, "id", 1)) || + (error = col_add_long_property(socket, NULL, "packets", 100000000L)) || + (error = col_add_binary_property(socket, NULL, "stack", binary_dump, sizeof(binary_dump)))) { + col_destroy_collection(peer); + col_destroy_collection(socket); + printf("Failed to add property. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket, COL_TRAVERSE_DEFAULT)); + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Adding PEER collection to SOCKET collection as a reference named PEER\n")); + + /* Embed peer host into the socket2 as reference */ + error = col_add_collection_to_collection(socket, NULL, "peer", peer, COL_ADD_MODE_REFERENCE); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(socket); + printf("Failed to create collection. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket, COL_TRAVERSE_DEFAULT)); + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(peer); + COLOUT(col_debug_collection(socket, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(socket); + TRACE_FLOW_NUMBER("add_collection_test. Returning", error); + return error; +} + +static int copy_cb(struct collection_item *item, + void *ext_data, + int *skip) +{ + COLOUT(printf("INSIDE Copy Callback\n")); + COLOUT(col_debug_item(item)); + COLOUT(printf("Passed in data: %s\n", (char *) ext_data)); + if (strcmp(col_get_item_property(item, NULL), "id") == 0) *skip = 1; + return EOK; +} + + +static int mixed_collection_test(void) +{ + struct collection_item *peer; + struct collection_item *socket1; + struct collection_item *socket2; + struct collection_item *socket3; + struct collection_item *event; + struct collection_item *host; + char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + int found = 0; + unsigned int class = 0; + char foo[] = "foo"; + + int error = EOK; + + TRACE_FLOW_STRING("mixed_collection_test", "Entry."); + + COLOUT(printf("\n\nMIXED TEST!!!.\n\n\n")); + COLOUT(printf("Creating PEER collection.\n")); + + if ((error = col_create_collection(&peer, "peer", 0)) || + (error = col_add_str_property(peer, NULL, "hostname", "peerhost.mytest.com", 0)) || + /* Expect trailing zero to be truncated */ + (error = col_add_str_property(peer, NULL, "IPv4", "10.10.10.10", 12)) || + (error = col_add_str_property(peer, NULL, "IPv6", "bla:bla:bla:bla:bla:bla", 0))) { + printf("Failed to add property. Error %d", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Creating HOST collection.\n")); + + if ((error = col_create_collection(&host, "host", 0)) || + (error = col_add_str_property(host, NULL, "hostname", "myhost.mytest.com", 0)) || + (error = col_add_str_property(host, NULL, "IPv4", "20.20.20.20", 13)) || + (error = col_add_str_property(host, NULL, "IPv6", "bla:bla:bla:bla:bla:bla", 0)) || + (error = col_add_double_property(host, NULL, "double", 0.253545))) { + printf("Failed to add property. Error %d", error); + col_destroy_collection(peer); + col_destroy_collection(host); + return error; + } + + COLOUT(col_debug_collection(host, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Creating SOCKET1 collection.\n")); + + if ((error = col_create_collection(&socket1, "socket1", 0)) || + (error = col_add_int_property(socket1, NULL, "id", 1)) || + (error = col_add_long_property(socket1, NULL, "packets", 100000000L)) || + (error = col_add_binary_property(socket1, NULL, "stack", binary_dump, sizeof(binary_dump)))) { + col_destroy_collection(peer); + col_destroy_collection(host); + col_destroy_collection(socket1); + printf("Failed to add property. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket1, COL_TRAVERSE_DEFAULT)); + COLOUT(printf("Creating a copy of SOCKET1 collection named SOCKET2.\n")); + + error = col_copy_collection(&socket2, socket1, "socket2", COL_COPY_NORMAL); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + col_destroy_collection(socket1); + printf("Failed to copy collection. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket2, COL_TRAVERSE_DEFAULT)); + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + + error = col_copy_collection_with_cb(&socket3, socket1, "socket3", + COL_COPY_FLATDOT, copy_cb, (void *)foo); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + printf("Failed to copy collection. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket3, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(socket3); + + COLOUT(printf("Adding PEER collection to SOCKET2 collection as a reference named PEER2\n")); + + /* Embed peer host into the socket2 as reference */ + error = col_add_collection_to_collection(socket2, NULL, "peer2", peer, COL_ADD_MODE_REFERENCE); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + printf("Failed to create collection. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket2, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Creating an EVENT collection.\n")); + + /* Construct event */ + error = col_create_collection(&event, "event", 0); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + printf("Failed to create collection. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(event, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Adding HOST to EVENT.\n")); + + /* Add host to event */ + error = col_add_collection_to_collection(event, NULL, NULL, host, COL_ADD_MODE_REFERENCE); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Failed to add collections. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(event, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Embed SOCKET1 into EVENT.\n")); + /* Donate socket1 to event */ + /* Socket1 should not be used after this */ + error = col_add_collection_to_collection(event, NULL, NULL, socket1, COL_ADD_MODE_EMBED); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Failed to add collections. Error %d\n", error); + return error; + } + + COLOUT(printf("Traverse one level:\n")); + COLOUT(col_debug_collection(event, COL_TRAVERSE_ONELEVEL)); + COLOUT(printf("Traverse ignore subcollections:\n")); + COLOUT(col_debug_collection(event, COL_TRAVERSE_IGNORE)); + COLOUT(printf("Traverse normal:\n")); + COLOUT(col_debug_collection(event, COL_TRAVERSE_DEFAULT)); + COLOUT(col_debug_collection(socket1, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("SOCKET1 MUST NOT BE USED AFTER THIS POINT!!!\n")); + socket1 = NULL; + + COLOUT(printf("Add collection PEER as PEER1 to subcollection SOCKET1 of the EVENT.\n")); + + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + + error = col_add_collection_to_collection(event, "socket1", "peer1", peer, COL_ADD_MODE_CLONE); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + /* No socket1 any more :) */ + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Failed to add collections. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(event, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Add property named TIMEOUT to PEER collection.\n")); + + /* Add new property to the peer collection */ + error = col_add_int_property(peer, NULL, "timeout", 5); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + /* No socket1 any more :) */ + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Failed to add property. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(socket2, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Add property named DELAY to PEER1 collection.\n")); + + error = col_add_int_property(event, "peer1", "delay", 10); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + /* No socket1 any more :) */ + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Failed to add property. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(event, COL_TRAVERSE_DEFAULT)); + COLOUT(col_debug_collection(host, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("Check if property PEER1.DELAY is in the EVENT collection.\n")); + + /* Check if the property in the collection */ + found = 0; + error = col_is_item_in_collection(event, "peer1!delay", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT, &found); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + /* No socket1 any more :) */ + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Failed to check property. Error %d\n", error); + return error; + } + + if (found == 1) { + COLOUT(printf("Property is found!\n")); + } + else { + COLOUT(printf("Error property is not found!\n")); + } + + + COLOUT(col_print_item(event, "peer1!IPv6")); + COLOUT(col_print_item(event, "event!socket1!peer1!IPv6")); + COLOUT(col_print_item(event, "event!peer1!IPv6")); + COLOUT(col_print_item(event, "speer1!IPv6")); + COLOUT(col_print_item(event, "eer1!IPv6")); + COLOUT(col_print_item(event, "!peer1!IPv6")); + COLOUT(col_print_item(event, "t!peer1!IPv6")); + + /* Traverse collection */ + if (verbose) { + error = col_print_collection2(event); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + /* No socket1 any more :) */ + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Error printing collection %d\n", error); + return error; + } + } + + COLOUT(printf("Delete property PEER1!DELAY from the EVENT collection.\n")); + + error = col_delete_property(event, "peer1!delay", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + /* No socket1 any more :) */ + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Failed to delete property. Error %d\n", error); + return error; + } + + COLOUT(printf("Printing EVENT.\n")); + + /* Traverse collection */ + if (verbose) { + error = col_print_collection2(event); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + /* No socket1 any more :) */ + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Error printing collection %d\n", error); + return error; + } + } + + COLOUT(printf("Debugging EVENT.\n")); + if (verbose) { + error = col_debug_collection(event, COL_TRAVERSE_DEFAULT); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(host); + /* No socket1 any more :) */ + col_destroy_collection(socket2); + col_destroy_collection(event); + printf("Error printing collection %d\n", error); + return error; + } + } + COLOUT(printf("Cleanup of the collections PEER, HOST and SOCKET2.\n")); + + /* Destroy a referenced collection */ + col_destroy_collection(peer); + col_destroy_collection(host); + col_destroy_collection(socket2); + + COLOUT(printf("Printing EVENT again.\n")); + + /* Traverse collection again - peer should still be there */ + if (verbose) { + error = col_print_collection(event); + if (error) { + col_destroy_collection(event); + printf("Error printing collection %d\n", error); + return error; + } + } + + COLOUT(printf("Debugging EVENT again.\n")); + + if (verbose) { + error = col_debug_collection(event, COL_TRAVERSE_DEFAULT); + if (error) { + col_destroy_collection(event); + printf("Error printing collection %d\n", error); + return error; + } + } + + COLOUT(printf("Attempt to add property to a referenced collection.\n")); + + error = col_add_int_property(event, "host", "session", 500); + if (error) { + col_destroy_collection(event); + printf("Error was NOT able to add property to a referenced collection %d.\n", error); + return error; + } + + COLOUT(printf("Attempt to delete non-existent property.\n")); + + /* Can't delete non exitent property */ + error = col_delete_property(event, "host.host", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT); + if (error == 0) { + col_destroy_collection(event); + printf("Error was able to delete property that does not exist.\n"); + return -1; + } + else COLOUT(printf("Expected error %d\n", error)); + + /* Set collection class */ + error = col_set_collection_class(event, 2); + if (error != 0) { + col_destroy_collection(event); + printf("Error was NOT able to set class.\n"); + return error; + } + + error = col_get_collection_class(event, &class); + if (error != 0) { + col_destroy_collection(event); + printf("Error was NOT able to get class.\n"); + return error; + } + else COLOUT(printf("Class = %d\n", class)); + + if (col_is_of_class(event, 2)) { + COLOUT(printf("Class mathced!\n")); + } + else { + col_destroy_collection(event); + printf("Error - bad class.\n"); + return -1; + } + + COLOUT(printf("Done. Cleaning...\n")); + + col_destroy_collection(event); + + COLOUT(printf("Exit.\n")); + TRACE_FLOW_NUMBER("add_collection_test. Returning", EOK); + return EOK; +} + + +static int iterator_test(void) +{ + struct collection_item *peer = NULL; + struct collection_item *initial = NULL; + + struct collection_item *socket1 = NULL; + struct collection_item *socket2 = NULL; + struct collection_item *socket3 = NULL; + struct collection_iterator *iterator = NULL; + int error = EOK; + struct collection_item *item = NULL; + char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + int depth = 0; + int idepth = 0; + int len = 0; + int i; + uint64_t hash1, hash2; + int rwnd = 0; + + COLOUT(printf("\n\n==== ITERATOR TEST ====\n\n")); + + if ((error = col_create_collection(&initial, "strater", 0)) || + (error = col_create_collection(&peer, "peer", 0)) || + (error = col_add_str_property(initial, NULL, "hostname", "peerhost.mytest.com", 0)) || + /* Expect trailing zero to be truncated */ + (error = col_add_str_property(initial, NULL, "IPv4", "10.10.10.10", 12)) || + (error = col_add_str_property(initial, NULL, "IPv6", "bla:bla:bla:bla:bla:bla", 0)) || + (error = col_add_collection_to_collection(peer, NULL, NULL, initial, COL_ADD_MODE_FLAT))) { + printf("Failed to add property. Error %d", error); + col_destroy_collection(peer); + col_destroy_collection(initial); + return error; + } + + col_destroy_collection(initial); + + if ((error = col_create_collection(&socket1, "socket", 0)) || + (error = col_add_int_property(socket1, NULL, "id", 1)) || + (error = col_add_long_property(socket1, NULL, "packets", 100000000L)) || + (error = col_add_binary_property(socket1, NULL, "stack", binary_dump, sizeof(binary_dump)))) { + col_destroy_collection(peer); + col_destroy_collection(socket1); + printf("Failed to add property. Error %d\n", error); + return error; + } + + if ((error = col_create_collection(&socket2, "socket", 0)) || + (error = col_add_int_property(socket2, NULL, "id", 2)) || + (error = col_add_long_property(socket2, NULL, "packets", 200000000L)) || + (error = col_add_binary_property(socket2, NULL, "queue", binary_dump, sizeof(binary_dump)))) { + col_destroy_collection(peer); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + printf("Failed to add property. Error %d\n", error); + return error; + } + + if ((error = col_create_collection(&socket3, "socket", 0))) { + col_destroy_collection(peer); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + printf("Failed to add property. Error %d\n", error); + return error; + } + + error = col_add_collection_to_collection(peer, NULL, "first", socket1, COL_ADD_MODE_REFERENCE); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + col_destroy_collection(socket3); + printf("Failed to add collection to collection. Error %d\n", error); + return error; + } + + error = col_add_collection_to_collection(peer, NULL, "second", socket2, COL_ADD_MODE_EMBED); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(socket1); + col_destroy_collection(socket2); + col_destroy_collection(socket3); + printf("Failed to add collection to collection. Error %d\n", error); + return error; + } + + error = col_add_collection_to_collection(peer, NULL, "third", socket3, COL_ADD_MODE_EMBED); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(socket1); + col_destroy_collection(socket3); + printf("Failed to add collection to collection. Error %d\n", error); + return error; + } + + error = col_add_collection_to_collection(peer, NULL, "forth", socket1, COL_ADD_MODE_FLATDOT); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(socket1); + printf("Failed to add collection to collection. Error %d\n", error); + return error; + } + + error = col_add_collection_to_collection(peer, NULL, NULL, socket1, COL_ADD_MODE_FLATDOT); + if (error) { + col_destroy_collection(peer); + col_destroy_collection(socket1); + printf("Failed to add collection to collection. Error %d\n", error); + return error; + } + + col_destroy_collection(socket1); + + /* Bind iterator */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_DEFAULT); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nCollection (traverse default):\n\n")); + COLOUT(col_debug_collection(peer, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("\n\nCollection (traverse flat):\n\n")); + COLOUT(col_debug_collection(peer, COL_TRAVERSE_FLAT | COL_TRAVERSE_END)); + + COLOUT(printf("\n\nIteration (1):\n\n")); + + do { + + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_unbind_iterator(iterator); + col_destroy_collection(peer); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + depth = 0; + col_get_item_depth(iterator, &depth); + idepth = 0; + col_get_iterator_depth(iterator, &idepth); + + + COLOUT(printf("%*sProperty (%s), type = %d, data size = %d depth = %d idepth = %d\n", + depth * 4, "", + col_get_item_property(item, NULL), + col_get_item_type(item), + col_get_item_length(item), + depth, + idepth)); + + if ((strcmp(col_get_item_property(item, NULL), "id")==0) && + (*((int *)(col_get_item_data(item))) == 1)) { + COLOUT(printf("\n\nFound property we need - go up!!!\n\n\n")); + + /* This should work! */ + error = col_iterate_up(iterator, 1); + if (error) { + printf("We expected success but got error %d\n", error); + col_unbind_iterator(iterator); + col_destroy_collection(peer); + return error; + } + + if ((error = col_modify_str_item(item, "id2", "test", 0)) || + ((verbose) && (error = col_debug_item(item))) || + (error = col_modify_str_item(item, NULL, "test", 2)) || + ((verbose) && (error = col_debug_item(item))) || + (error = col_modify_binary_item(item, NULL, binary_dump, sizeof(binary_dump))) || + ((verbose) && (error = col_debug_item(item))) || + (error = col_modify_bool_item(item, NULL, 1)) || + ((verbose) && (error = col_debug_item(item))) || + (error = col_modify_int_item(item, "int", 1)) || + ((verbose) && (error = col_debug_item(item))) || + (error = col_modify_long_item(item, "long", 1000000000L)) || + ((verbose) && (error = col_debug_item(item))) || + (error = col_modify_ulong_item(item, "ulong", 4000000000UL)) || + ((verbose) && (error = col_debug_item(item))) || + (error = col_modify_unsigned_item(item, "unsigned", 4000000000U)) || + ((verbose) && (error = col_debug_item(item))) || + (error = col_modify_double_item(item, "double", -1.1)) || + ((verbose) && (error = col_debug_item(item)))) { + printf("Failed to change property.\n"); + col_unbind_iterator(iterator); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("Item name: %s\n", col_get_item_property(item, NULL))); + COLOUT(printf("Item hash: %lu\n", (unsigned long int)col_get_item_hash(item))); + error = col_modify_item_property(item, "new_name"); + if (error) { + printf("We expected success but got error %d\n", error); + col_unbind_iterator(iterator); + col_destroy_collection(peer); + return error; + } + len = 0; + COLOUT(printf("Item name: %s\n", col_get_item_property(item, &len))); + COLOUT(printf("Item hash: %lu\n", (unsigned long int)col_get_item_hash(item))); + COLOUT(printf("Item length: %d\n", len)); + + len = 0; + hash1 = col_make_hash("new_name", 0, &len); + COLOUT(printf("String name: %s\n", "new_name")); + COLOUT(printf("String hash: %lu\n", (unsigned long int)hash1)); + COLOUT(printf("String length: %d\n", len)); + + len = 0; + hash2 = col_make_hash("new_name_suffix", 8, &len); + COLOUT(printf("String name: %.*s\n", len, "new_name_suffix")); + COLOUT(printf("String hash: %lu\n", (unsigned long int)hash2)); + COLOUT(printf("String length: %d\n", len)); + if (hash1 != hash2) { + printf("Hash calculation failed\n"); + col_unbind_iterator(iterator); + col_destroy_collection(peer); + return EINVAL; + } + + hash2 = col_make_hash("new_name", 8, &len); + COLOUT(printf("String name: %.*s\n", len, "new_name")); + COLOUT(printf("String hash: %lu\n", (unsigned long int)hash2)); + COLOUT(printf("String length: %d\n", len)); + if (hash1 != hash2) { + printf("Hash calculation failed\n"); + col_unbind_iterator(iterator); + col_destroy_collection(peer); + return EINVAL; + } + + } + } + while(1); + + col_unbind_iterator(iterator); + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_FLAT); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nIteration (2 - flat):\n\n")); + + do { + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + + if ((strcmp(col_get_item_property(item, NULL), "queue") == 0) && + (rwnd == 0)) { + COLOUT(printf("Rewinding iterator...\n")); + col_rewind_iterator(iterator); + rwnd++; + } + + } + while(1); + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_FLAT | COL_TRAVERSE_END); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nIteration (3 flat with end):\n\n")); + + do { + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + + } + while(1); + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nIteration (4 default with end):\n\n")); + + do { + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + + } + while(1); + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_SHOWSUB | COL_TRAVERSE_END); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + + COLOUT(printf("\n\nIteration (5 show headers and references with end):\n\n")); + + do { + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + + } + while(1); + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_SHOWSUB); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + + COLOUT(printf("\n\nIteration (6 show headers and references no END):\n\n")); + + do { + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + + } + while(1); + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_ONLYSUB); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nIteration (7 show headers only no END):\n\n")); + + do { + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + + } + while(1); + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + + /* Bind iterator */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_DEFAULT); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nIterate up test:\n\n")); + + do { + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_unbind_iterator(iterator); + col_destroy_collection(peer); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + depth = 0; + col_get_item_depth(iterator, &depth); + idepth = 0; + col_get_iterator_depth(iterator, &idepth); + + + COLOUT(printf("%*sProperty (%s), type = %d, data size = %d depth = %d idepth = %d\n", + depth * 4, "", + col_get_item_property(item, NULL), + col_get_item_type(item), + col_get_item_length(item), + depth, + idepth)); + + if (strcmp(col_get_item_property(item, NULL), "queue") == 0) { + + COLOUT(printf("\n\nFound property we need - go up!!!\n")); + COLOUT(printf("Expect bail out of collection processing.\n\n")); + + /* This should work! */ + error = col_iterate_up(iterator, 10); + if (error) { + printf("We expected success but got error %d\n", error); + col_unbind_iterator(iterator); + col_destroy_collection(peer); + return error; + } + + } + } + while(1); + + col_unbind_iterator(iterator); + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_FLAT | COL_TRAVERSE_END); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nCircled looping:\n\n")); + + for (i = 0; i < 200; i++) { + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) { + COLOUT(printf("Reached end.\n\n")); + } + else { + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + } + } + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_FLAT | COL_TRAVERSE_END); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nCircled looping with pin:\n\n")); + + do { + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + if (strcmp(col_get_item_property(item, NULL), "queue") == 0) { + /* Make it a new looping point */ + col_pin_iterator(iterator); + COLOUT(printf("Found pin point.\n\n")); + break; + } + /* Are we done ? */ + if (item == NULL) { + printf("Unexpected end.\n\n"); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return EINVAL; + } + else { + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + } + } + while(1); + + /* Second loop around the pin point */ + for (i = 0; i < 200; i++) { + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) { + COLOUT(printf("Reached end.\n\n")); + } + else { + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + } + } + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + + /* Bind iterator again in flat mode */ + error = col_bind_iterator(&iterator, peer, COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END); + if (error) { + printf("Error (bind): %d\n", error); + col_destroy_collection(peer); + return error; + } + + COLOUT(printf("\n\nCircled looping with pin (default):\n\n")); + + do { + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + if (strcmp(col_get_item_property(item, NULL), "queue") == 0) { + /* Make it a new looping point */ + col_pin_iterator(iterator); + COLOUT(printf("Found pin point.\n\n")); + break; + } + /* Are we done ? */ + if (item == NULL) { + printf("Unexpected end.\n\n"); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return EINVAL; + } + else { + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + } + } + while(1); + + /* Second loop around the pin point */ + for (i = 0; i < 200; i++) { + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + printf("Error (iterate): %d\n", error); + col_destroy_collection(peer); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) { + COLOUT(printf("Reached end.\n\n")); + } + else { + depth = 0; + col_get_item_depth(iterator, &depth); + COLOUT(printf("%*s", depth * 4, "")); + COLOUT(col_debug_item(item)); + } + } + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + col_destroy_collection(peer); + + return EOK; +} + +static int validate_collection(struct collection_item *col, + const char *varray[][2]) +{ + struct collection_iterator *iterator = NULL; + int error = EOK; + struct collection_item *item = NULL; + int count = -1; + + COLOUT(printf("\n\n==== Validating collection ====\n\n")); + + /* Bind iterator */ + error = col_bind_iterator(&iterator, col, COL_TRAVERSE_DEFAULT); + if (error) { + COLOUT(printf("Error (bind): %d\n", error)); + return error; + } + + do { + + count++; + + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + COLOUT(printf("Error (iterate): %d\n", error)); + col_unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if (item == NULL) break; + + /* Set count when we skip header */ + if (col_get_item_type(item) == COL_TYPE_COLLECTION) { + count = -1; + continue; + } + + if (varray[count][0] == NULL) { + /* We have reached the end of the array but not the collection. + * This means the test failed. Return error. + */ + COLOUT(printf("Got extra item in the collection: %s=%s\n", + col_get_item_property(item, NULL), + (char *)(col_get_item_data(item)))); + col_unbind_iterator(iterator); + return -1; + } + + if ((strcmp(col_get_item_property(item, NULL), + varray[count][0]) != 0) || + (strcmp((char *)(col_get_item_data(item)), + varray[count][1]) != 0 )) { + COLOUT(printf("Expected %s=\"%s\" got %s=\"%s\" \n", + varray[count][0], + varray[count][1], + col_get_item_property(item, NULL), + (char *)(col_get_item_data(item)))); + col_unbind_iterator(iterator); + return -1; + } + } + while(1); + + col_unbind_iterator(iterator); + + if (varray[count][0] != NULL) { + COLOUT(printf("Expected end got %s=\"%s\"\n", varray[count][0], + varray[count][1])); + return -1; + } + + COLOUT(printf("\n\n==== Validating collection - OK ====\n\n")); + return EOK; +} + + +static int insert_extract_test(void) +{ + struct collection_item *col; + struct collection_item *col2; + int error = EOK; + struct collection_item *item = NULL; + const char *varray1[][2] = { { "property_-1", "value_-1" }, + { "property0", "value0lastdup" }, + { "property0_5", "value0_5" }, + { "property1", "value1update" }, + { "second", "second" }, + { "property1_5", "value1_5" }, + { "property2", "value2" }, + { "property10", "value10" }, + { "property10", "value10lastdup" }, + { "property_-2", "value-2moved_to_bottom" }, + { "property0", "extra_1" }, + { "property100", "value100" }, + { NULL, NULL } }; + + const char *varray2[][2] = { { "property_-1_5","value_-1_5" }, + { "property1_6", "value_1_6_moved_to_front" }, + { "property0", "very_last" }, + { "property10", "value10dup" }, + { "property_-0_5", "value_-0_5" }, + { "property0", "value0firstdupupdate" }, + { "property0", "value0" }, + { "property0", "before 0_5" }, + { "property0", "value0middledup" }, + { "property0", "extra_2" }, + { "property0", "after_extra2" }, + { "property0", "before_extra2" }, + { NULL, NULL } }; + + + COLOUT(printf("\n\n==== INSERTION TEST ====\n\n")); + + if ((error = col_create_collection(&col, "insertion", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK, + "property1", "value1", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_INDEX, + NULL, 1, COL_INSERT_NOCHECK, + "second", "second", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK, + "property2", "value2", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_FRONT, + NULL, 0, COL_INSERT_NOCHECK, + "property0", "value0", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_BEFORE, + "property0", 0, COL_INSERT_NOCHECK, + "property_-1", "value_-1", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_BEFORE, + "property1", 0, COL_INSERT_NOCHECK, + "property0_5", "value0_5", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_BEFORE, + "property2", 0, COL_INSERT_NOCHECK, + "property1_5", "value1_5", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_AFTER, + "property_-1", 0, COL_INSERT_NOCHECK, + "property_-0_5", "value_-0_5", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_AFTER, + "property1_5", 0, COL_INSERT_NOCHECK, + "property1_6", "value1_6", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_INDEX, + NULL, 10, COL_INSERT_NOCHECK, + "property10", "value10", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_INDEX, + NULL, 0, COL_INSERT_NOCHECK, + "property_-2", "value_-2", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_INDEX, + NULL, 1, COL_INSERT_NOCHECK, + "property_-1_5", "value_-1_5", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_FIRSTDUP, + NULL, 0, COL_INSERT_NOCHECK, + "property0", "value0firstdup", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_LASTDUP, + NULL, 0, COL_INSERT_NOCHECK, + "property0", "value0lastdup", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_NDUP, + NULL, 1, COL_INSERT_NOCHECK, + "property0", "value0middledup", 0)) || + (error = col_insert_str_property(col, NULL, 0, + NULL, 0, COL_INSERT_DUPOVER , + "property0", + "value0firstdupupdate", 0)) || + (error = col_insert_str_property(col, NULL, 0, + NULL, 0, COL_INSERT_DUPOVERT, + "property1", "value1update", 0)) || + ((error = col_insert_str_property(col, NULL, 0, + NULL, 0, COL_INSERT_DUPERROR, + "property0", + "does not matter", 0)) != EEXIST) || + (error = col_insert_str_property(col, NULL, COL_DSP_NDUP, + NULL, 5, COL_INSERT_NOCHECK, + "property10", "value10dup", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_LASTDUP, + NULL, 0, COL_INSERT_NOCHECK, + "property10", "value10lastdup", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_END, + NULL, 0, COL_INSERT_DUPMOVET, + "property_-2", + "value-2moved_to_bottom", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_FRONT, + NULL, 0, COL_INSERT_DUPMOVE, + "property1_6", + "value_1_6_moved_to_front", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK, + "property0", "extra_1", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK, + "property0", "extra_2", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_NDUP, + "property0", 10, COL_INSERT_NOCHECK, + "property0", "before 0_5", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_NDUPNS, + "property0", 10, COL_INSERT_NOCHECK, + "property0", "after_extra2", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_NDUPNS, + "property0", 6, COL_INSERT_NOCHECK, + "property0", "before_extra2", 0)) || + (error = col_insert_str_property(col, NULL, COL_DSP_LASTDUPNS, + "property0", 0, COL_INSERT_NOCHECK, + "property0", "very_last", 0))) { + + printf("ERROR in the ITERATION TEST %d\n", error); + col_debug_collection(col, COL_TRAVERSE_DEFAULT); + col_destroy_collection(col); + return error; + } + + COLOUT(printf("\n\nCollection:\n\n")); + COLOUT(col_debug_collection(col, COL_TRAVERSE_DEFAULT)); + + + COLOUT(printf("\n\n==== EXTRACTION TEST ====\n\n")); + + if ((error = col_create_collection(&col2, "extraction", 0)) || + + /* Extracting "property 1_6" value "value_1_6_moved_to_front" */ + (error = col_extract_item(col, NULL, COL_DSP_FRONT, + NULL, 0, 0, &item)) || + + (error = col_insert_item(col2, NULL, item, COL_DSP_FRONT, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Extracting "property_-1_5" value "value_-1_5" */ + (error = col_extract_item(col, NULL, COL_DSP_FRONT, + NULL, 0, 0, &item)) || + + /* Putting it in front */ + (error = col_insert_item(col2, NULL, item, COL_DSP_FRONT, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Extracting "property0" value "very_last" */ + (error = col_extract_item(col, NULL, COL_DSP_END, + NULL, 0, 0, &item)) || + + /* Putting it to the end */ + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Inserting extra property to the end of first collection */ + (error = col_insert_str_property(col, NULL, COL_DSP_INDEX, + NULL, 100, COL_INSERT_NOCHECK, + "property100", "value100", 0)) || + + /* This will extract "property10" with value "value10dup" */ + (error = col_extract_item(col, NULL, COL_DSP_AFTER, + "property10", 0, COL_TYPE_STRING, &item)) || + + /* Putting it to the end */ + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Extracting "property_-0_5" value "value_-0_5" */ + (error = col_extract_item(col, NULL, COL_DSP_BEFORE, + "property0", 0, COL_TYPE_STRING, &item)) || + + /* Putting it to the end */ + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + /* Printing initial collection to see its state */ + ((verbose) && (error = col_debug_collection(col, + COL_TRAVERSE_DEFAULT))) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Going for the second item in the collection which is + * at this moment "property0" with value "value0firstdupupdate" + */ + (error = col_extract_item(col, NULL, COL_DSP_INDEX, + NULL, 1, 0, &item)) || + + /* Putting it to the end */ + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Extracting first sequextial duplicate of property0 whic is value + * "value0" + */ + (error = col_extract_item(col, NULL, COL_DSP_NDUP, + "property0", 1, 0, &item)) || + + /* Putting it to the end */ + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Extracting last sequential dup of "property0" + * which is at this moment "before 0_5" + */ + (error = col_extract_item(col, NULL, COL_DSP_LASTDUP, + "property0", 0, 0, &item)) || + + /* Putting it to the end */ + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Extracting first dup of "property0" + * which is at this moment "value0middledup" + */ + (error = col_extract_item(col, NULL, COL_DSP_FIRSTDUP, + "property0", 0, 0, &item)) || + + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Printing initial collection to see its state */ + ((verbose) && (error = col_debug_collection(col, + COL_TRAVERSE_DEFAULT))) || + + /* Should extract extra_2 */ + (error = col_extract_item(col, NULL, COL_DSP_NDUPNS, + "property0", 3, 0, &item)) || + + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Printing initial collection to see its state */ + ((verbose) && (error = col_debug_collection(col, + COL_TRAVERSE_DEFAULT))) || + + /* Try extracting something that is out of index, expect ENOENT */ + ((error = col_extract_item(col, NULL, COL_DSP_NDUPNS, + "property0", 10, 0, &item)) != ENOENT) || + + /* Should extract after_extra_2 */ + (error = col_extract_item(col, NULL, COL_DSP_NDUPNS, + "property0", 3, 0, &item)) || + + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT))) || + + /* Printing initial collection to see its state */ + ((verbose) && (error = col_debug_collection(col, + COL_TRAVERSE_DEFAULT))) || + + /* Should extract before_extra_2 */ + (error = col_extract_item(col, NULL, COL_DSP_LASTDUPNS, + "property0", 0, 0, &item)) || + + (error = col_insert_item(col2, NULL, item, COL_DSP_END, + NULL, 0, COL_INSERT_NOCHECK)) || + + ((verbose) && (error = col_debug_collection(col2, + COL_TRAVERSE_DEFAULT)))) { + + COLOUT(printf("ERROR in the EXTRACTION TEST\n")); + COLOUT(printf("Collection 1\n")); + COLOUT(col_debug_collection(col, COL_TRAVERSE_DEFAULT)); + COLOUT(printf("Collection 2\n")); + COLOUT(col_debug_collection(col2, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(col); + col_destroy_collection(col2); + return error; + } + + COLOUT(printf("Collection 1\n")); + COLOUT(col_debug_collection(col, COL_TRAVERSE_DEFAULT)); + COLOUT(printf("Collection 2\n")); + COLOUT(col_debug_collection(col2, COL_TRAVERSE_DEFAULT)); + + error = validate_collection(col, varray1); + if (error) { + COLOUT(printf("Collection 1 validation failed.\n")); + col_destroy_collection(col); + col_destroy_collection(col2); + return error; + } + + error = validate_collection(col2, varray2); + if (error) { + COLOUT(printf("Collection 2 validation failed.\n")); + col_destroy_collection(col); + col_destroy_collection(col2); + return error; + } + + col_destroy_collection(col2); + col_destroy_collection(col); + + return EOK; +} + +/* Cleanup collback */ +static void cb(const char *property, + int property_len, + int type, + void *data, + int length, + void *ext_data) +{ + COLOUT(printf("%s\n", *((const char **)ext_data))); + COLOUT(printf("Property: %s\n", property)); + COLOUT(printf("Length: %d\n", property_len)); + COLOUT(printf("Type: %d\n", type)); + COLOUT(printf("Data len: %d\n", length)); +} + +static int delete_test(void) +{ + + struct collection_item *col; + int error = EOK; + const char *str = "Cleanup Callback Test"; + + COLOUT(printf("\n\n==== DELETION TEST 1====\n\n")); + + if ((error = col_create_collection(&col, "test", 0)) || + (error = col_add_int_property(col, NULL, "tt", 1)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_add_int_property(col, NULL, "test", 1)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_delete_property(col, "test", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_add_int_property(col, NULL, "test", 1)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_delete_property(col, "test", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_add_int_property(col, NULL, "test", 1))) { + printf("Error in delete test %d\n", error); + col_destroy_collection(col); + return error; + } + + COLOUT(col_debug_collection(col, COL_TRAVERSE_DEFAULT)); + col_destroy_collection(col); + + COLOUT(printf("\n\n==== DELETION TEST 1 END ====\n\n")); + COLOUT(printf("\n\n==== DELETION TEST 2====\n\n")); + + if ((error = col_create_collection(&col, "test2", 0)) || + (error = col_add_int_property(col, NULL, "tt", 1)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_add_int_property(col, NULL, "test", 1)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_remove_item(col, NULL, COL_DSP_END, NULL, 0, COL_TYPE_ANY)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_add_int_property(col, NULL, "test", 1)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_remove_item_from_current(col, COL_DSP_AFTER, "tt", 0, COL_TYPE_ANY)) || + ((verbose) && (error = col_debug_collection(col, COL_TRAVERSE_DEFAULT))) || + (error = col_add_int_property(col, NULL, "test", 1))) { + printf("Error in delete test %d\n", error); + col_destroy_collection(col); + return error; + } + + COLOUT(col_debug_collection(col, COL_TRAVERSE_DEFAULT)); + + COLOUT(printf("\n\n==== DELETION TEST 2 END ====\n\n")); + + + col_destroy_collection_with_cb(col, cb, (void *)(&str)); + + return error; +} + +/* Search test */ +static int search_test(void) +{ + struct collection_item *level1 = NULL; + struct collection_item *level2 = NULL; + struct collection_item *level3 = NULL; + struct collection_item *level4 = NULL; + char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + int found = 0; + int error = 0; + + COLOUT(printf("\n\n==== SEARCH TEST ====\n\n")); + + if ((error = col_create_collection(&level1, "level1", 0)) || + (error = col_create_collection(&level2, "level2", 0)) || + (error = col_add_collection_to_collection(level1, NULL, NULL, level2, COL_ADD_MODE_REFERENCE)) || + (error = col_create_collection(&level3, "level3", 0)) || + (error = col_add_collection_to_collection(level1, "level2", NULL, level3, COL_ADD_MODE_REFERENCE)) || + (error = col_create_collection(&level4, "leveL4", 0)) || + (error = col_add_collection_to_collection(level1, "level3", NULL, level4, COL_ADD_MODE_REFERENCE)) || + (error = col_add_int_property(level1, "leveL4", "id", 1)) || + (error = col_add_long_property(level1, "level3", "packets", 100000000L)) || + (error = col_add_binary_property(level1, "level2", "stack", binary_dump, sizeof(binary_dump)))) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Failed to build test. Error %d\n", error); + return error; + } + + COLOUT(col_debug_collection(level1, COL_TRAVERSE_DEFAULT)); + + error = col_is_item_in_collection(level1, "level1!level2!level3!level4!", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT, &found); + if (!error) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Expected error here since the search data is illegal but got success\n"); + return EINVAL; + } + + found = 0; + error = col_is_item_in_collection(level1, "level1!level2!level3!level4!id", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT, &found); + if ((error) || (!found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Failed to find item [level1!level2!level3!level4!id]. Error %d\n", error); + return error ? error : ENOENT; + } + else COLOUT(printf("Expected item is found\n")); + + + found = 0; + error = col_is_item_in_collection(level1, NULL, COL_TYPE_INTEGER, COL_TRAVERSE_DEFAULT, &found); + if ((error) || (!found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Failed to find first int item [level1!level2!level3!level4!id]. Error %d\n", error); + return error ? error : ENOENT; + } + else COLOUT(printf("Expected item is found\n")); + + + found = 0; + error = col_is_item_in_collection(level1, "", COL_TYPE_INTEGER, COL_TRAVERSE_DEFAULT, &found); + if ((error) || (!found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Failed to find first int item [level1!level2!level3!level4!id]. Error %d\n", error); + return error ? error : ENOENT; + } + else COLOUT(printf("Expected item is found\n")); + + + found = 0; + error = col_is_item_in_collection(level1, "level3!level4!id", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT, &found); + if ((error) || (!found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Failed to find item [level3!level4!id]. Error %d\n", error); + return error ? error : ENOENT; + } + else COLOUT(printf("Expected item is found\n")); + + found = 0; + error = col_is_item_in_collection(level1, "level3!packets", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT, &found); + if ((error) || (!found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Failed to find item [level3.packets]. Error %d\n", error); + return error ? error : ENOENT; + } + else COLOUT(printf("Expected item is found\n")); + + found = 0; + error = col_is_item_in_collection(level1, "level1!level2!stack", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT, &found); + if ((error) || (!found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Failed to find item [level1!level2!stack]. Error %d\n", error); + return error ? error : ENOENT; + } + else COLOUT(printf("Expected item is found\n")); + + found = 0; + error = col_is_item_in_collection(level1, "level1!level2!level3", COL_TYPE_ANY, COL_TRAVERSE_DEFAULT, &found); + if ((error) || (!found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + printf("Failed to find item [level1!level2!level3]. Error %d\n", error); + return error ? error : ENOENT; + } + else COLOUT(printf("Expected item is found\n")); + + /* Negative tests */ + found = 0; + error = col_is_item_in_collection(level1, NULL, 0, COL_TRAVERSE_DEFAULT, &found); + if ((error != ENOENT) || (found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + if (error) { + printf("Unexpected error with NULL & 0 test %d\n", error); + } + else { + printf("Found unexpected item with NULL & 0. Error %d\n", error); + error = EINVAL; + } + return error; + } + else COLOUT(printf("No item is found as expected.\n")); + + found = 0; + error = col_is_item_in_collection(level1, "", 0, COL_TRAVERSE_DEFAULT, &found); + if ((error != ENOENT) || (found)) { + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + if (error) { + printf("Unexpected error with \"\" & 0 tests %d\n", error); + } + else { + printf("Found unexpected item with \"\" & 0. Error %d\n", error); + error = EINVAL; + } + return error; + } + else COLOUT(printf("No item is found as expected.\n")); + + col_destroy_collection(level1); + col_destroy_collection(level2); + col_destroy_collection(level3); + col_destroy_collection(level4); + + COLOUT(printf("\n\n==== SEARCH TEST END ====\n\n")); + + return EOK; +} + +/* Sort test */ +static int sort_test(void) +{ + struct collection_item *level1 = NULL; + struct collection_item *level2a = NULL; + struct collection_item *level2b = NULL; + struct collection_item *level3 = NULL; + int error = 0; + + COLOUT(printf("\n\n==== SORT TEST ====\n\n")); + + if ((error = col_create_collection(&level1, "level1", 0)) || + (error = col_create_collection(&level2a, "level2a", 0)) || + (error = col_add_collection_to_collection(level1, NULL, NULL, level2a, COL_ADD_MODE_REFERENCE)) || + (error = col_create_collection(&level2b, "level2b", 0)) || + (error = col_add_collection_to_collection(level1, NULL, NULL, level2b, COL_ADD_MODE_REFERENCE)) || + (error = col_create_collection(&level3, "level3", 0)) || + (error = col_add_collection_to_collection(level1, "level2a", NULL, level3, COL_ADD_MODE_REFERENCE)) || + (error = col_add_collection_to_collection(level1, "level2b", NULL, level3, COL_ADD_MODE_REFERENCE)) || + (error = col_add_int_property(level1, NULL, "int3", 1)) || + (error = col_add_int_property(level1, NULL, "int2", 2)) || + (error = col_add_int_property(level1, NULL, "int1", 3)) || + (error = col_add_bool_property(level1, NULL, "bool3", 1)) || + (error = col_add_bool_property(level1, NULL, "bool2", 1)) || + (error = col_add_bool_property(level1, NULL, "bool1", 0)) || + (error = col_add_unsigned_property(level1, NULL, "unsigned1", 2)) || + (error = col_add_unsigned_property(level1, NULL, "unsigned3", 1)) || + (error = col_add_unsigned_property(level1, NULL, "unsigned2", 3)) || + (error = col_add_long_property(level1, NULL, "long3", 1)) || + (error = col_add_long_property(level1, NULL, "long2", 2)) || + (error = col_add_long_property(level1, NULL, "long1", 3)) || + (error = col_add_ulong_property(level1, NULL, "ulong1", 2)) || + (error = col_add_ulong_property(level1, NULL, "ulong3", 1)) || + (error = col_add_ulong_property(level1, NULL, "ulong2", 3)) || + (error = col_add_double_property(level1, NULL, "double1", 2.2)) || + (error = col_add_double_property(level1, NULL, "double3", 1.1)) || + (error = col_add_double_property(level1, NULL, "double2", 3.3)) || + (error = col_add_int_property(level3, NULL, "int3L3", 1)) || + (error = col_add_int_property(level3, NULL, "int2L3", 2)) || + (error = col_add_int_property(level3, NULL, "int1L3", 3)) || + (error = col_add_unsigned_property(level1, "level2a!level3", "unsigned1L3", 2)) || + (error = col_add_unsigned_property(level1, "level2a!level3", "unsigned3L3", 1)) || + (error = col_add_unsigned_property(level1, "level2a!level3", "unsigned2L3", 3)) || + (error = col_add_long_property(level1, "level2b!level3", "long3L3", 1)) || + (error = col_add_long_property(level1, "level2b!level3", "long2L3", 2)) || + (error = col_add_long_property(level1, "level2b!level3", "long1L3", 3)) || + (error = col_add_ulong_property(level1, "level3", "ulong1L3", 2)) || + (error = col_add_ulong_property(level1, "level3", "ulong3L3", 1)) || + (error = col_add_ulong_property(level1, "level3", "ulong2L3", 3)) || + (error = col_add_bool_property(level3, NULL, "bool3", 1)) || + (error = col_add_bool_property(level3, NULL, "bool2", 1)) || + (error = col_add_bool_property(level3, NULL, "bool1", 0)) || + (error = col_add_double_property(level3, NULL, "double1L3", 2.2)) || + (error = col_add_double_property(level3, NULL, "double3L3", 1.1)) || + (error = col_add_double_property(level3, NULL, "double2L3", 3.3))) { + col_destroy_collection(level1); + col_destroy_collection(level2a); + col_destroy_collection(level2b); + col_destroy_collection(level3); + printf("Failed to build test. Error %d\n", error); + return error; + } + + COLOUT(printf("\nUNSORTED COLLECTION\n\n")); + COLOUT(col_debug_collection(level1, COL_TRAVERSE_DEFAULT)); + + error = col_sort_collection(level1, COL_CMPIN_PROP_EQU, COL_SORT_SUB | COL_SORT_MYSUB); + if (error) { + col_destroy_collection(level1); + col_destroy_collection(level2a); + col_destroy_collection(level2b); + col_destroy_collection(level3); + printf("Failed sort. Error %d\n", error); + return error; + } + + COLOUT(printf("\nSORTED BUT SKIPPING REFERENCES\n\n")); + COLOUT(col_debug_collection(level1, COL_TRAVERSE_DEFAULT)); + + error = col_sort_collection(level1, COL_CMPIN_PROP_EQU, COL_SORT_SUB); + if (error) { + col_destroy_collection(level1); + col_destroy_collection(level2a); + col_destroy_collection(level2b); + col_destroy_collection(level3); + printf("Failed sort. Error %d\n", error); + return error; + } + + COLOUT(printf("\nSORTED BUT NOT SKIPPING REFERENCES\n\n")); + COLOUT(col_debug_collection(level1, COL_TRAVERSE_DEFAULT)); + + error = col_sort_collection(level1, COL_CMPIN_DATA_LEN, COL_SORT_SUB | COL_SORT_DESC); + if (error) { + col_destroy_collection(level1); + col_destroy_collection(level2a); + col_destroy_collection(level2b); + col_destroy_collection(level3); + printf("Failed sort. Error %d\n", error); + return error; + } + + COLOUT(printf("\nSORTED DESC NOT SKIPPING BY LENGTH OF DATA\n\n")); + COLOUT(col_debug_collection(level1, COL_TRAVERSE_DEFAULT)); + + error = col_sort_collection(level1, COL_CMPIN_PROP_LEN, COL_SORT_SUB | COL_SORT_DESC); + if (error) { + col_destroy_collection(level1); + col_destroy_collection(level2a); + col_destroy_collection(level2b); + col_destroy_collection(level3); + printf("Failed sort. Error %d\n", error); + return error; + } + + COLOUT(printf("\nSORTED DESC NOT SKIPPING BY LENGTH OF PROPERTY\n\n")); + COLOUT(col_debug_collection(level1, COL_TRAVERSE_DEFAULT)); + + error = col_sort_collection(level1, COL_CMPIN_DATA, COL_SORT_SUB | COL_SORT_DESC); + if (error) { + col_destroy_collection(level1); + col_destroy_collection(level2a); + col_destroy_collection(level2b); + col_destroy_collection(level3); + printf("Failed sort. Error %d\n", error); + return error; + } + + COLOUT(printf("\nSORTED DESC NOT SKIPPING BY DATA\n\n")); + COLOUT(col_debug_collection(level1, COL_TRAVERSE_DEFAULT)); + + col_destroy_collection(level1); + col_destroy_collection(level2a); + col_destroy_collection(level2b); + col_destroy_collection(level3); + + COLOUT(printf("\n\n==== SORT TEST END ====\n\n")); + + return EOK; +} + +/* Main function of the unit test */ + +int main(int argc, char *argv[]) +{ + int error = 0; + test_fn tests[] = { ref_collection_test, + single_collection_test, + add_collection_test, + mixed_collection_test, + iterator_test, + insert_extract_test, + delete_test, + search_test, + sort_test, + dup_test, + NULL }; + test_fn t; + int i = 0; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + + printf("Start\n"); + + while ((t = tests[i++])) { + error = t(); + if (error) { + printf("Failed!\n"); + return error; + } + } + + printf("Success!\n"); + return 0; + +} diff --git a/tests/sanity/dhash_ut_check.c b/tests/sanity/dhash_ut_check.c new file mode 100644 index 0000000..357518d --- /dev/null +++ b/tests/sanity/dhash_ut_check.c @@ -0,0 +1,224 @@ +/* + Authors: + Michal Zidek + + Copyright (C) 2016 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include + +/* #define TRACE_LEVEL 7 */ +#define TRACE_HOME +#include "dhash.h" + +#define HTABLE_SIZE 128 + +int verbose = 0; + +/* There must be no warnings generated during this test + * without having to cast the key value. */ +START_TEST(test_key_const_string) +{ + hash_table_t *htable; + int ret; + hash_value_t ret_val; + hash_value_t enter_val1; + hash_value_t enter_val2; + hash_key_t key; + + enter_val1.type = HASH_VALUE_INT; + enter_val1.i = 1; + enter_val2.type = HASH_VALUE_INT; + enter_val2.i = 2; + key.type = HASH_KEY_CONST_STRING; + key.c_str = "constant"; + + ret = hash_create(HTABLE_SIZE, &htable, NULL, NULL); + fail_unless(ret == 0); + + /* The table is empty, lookup should return error */ + ret = hash_lookup(htable, &key, &ret_val); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + /* Deleting with non-existing key should return error */ + ret = hash_delete(htable, &key); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + ret = hash_enter(htable, &key, &enter_val1); + fail_unless(ret == 0); + + hash_lookup(htable, &key, &ret_val); + fail_unless(ret == 0); + fail_unless(ret_val.i == 1); + + /* Overwrite the entry */ + ret = hash_enter(htable, &key, &enter_val2); + fail_unless(ret == 0); + + hash_lookup(htable, &key, &ret_val); + fail_unless(ret == 0); + fail_unless(ret_val.i == 2); + + ret = hash_delete(htable, &key); + fail_unless(ret == 0); + + /* Delete again with the same key */ + ret = hash_delete(htable, &key); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + ret = hash_destroy(htable); + fail_unless(ret == 0); +} +END_TEST + +START_TEST(test_key_string) +{ + hash_table_t *htable; + int ret; + hash_value_t ret_val; + hash_value_t enter_val1; + hash_value_t enter_val2; + hash_key_t key; + char str[] = "non_constant"; + + enter_val1.type = HASH_VALUE_INT; + enter_val1.i = 1; + enter_val2.type = HASH_VALUE_INT; + enter_val2.i = 2; + key.type = HASH_KEY_STRING; + key.str = str; + + ret = hash_create(HTABLE_SIZE, &htable, NULL, NULL); + fail_unless(ret == 0); + + /* The table is empty, lookup should return error */ + ret = hash_lookup(htable, &key, &ret_val); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + /* Deleting with non-existing key should return error */ + ret = hash_delete(htable, &key); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + ret = hash_enter(htable, &key, &enter_val1); + fail_unless(ret == 0); + + hash_lookup(htable, &key, &ret_val); + fail_unless(ret == 0); + fail_unless(ret_val.i == 1); + + /* Overwrite the entry */ + ret = hash_enter(htable, &key, &enter_val2); + fail_unless(ret == 0); + + hash_lookup(htable, &key, &ret_val); + fail_unless(ret == 0); + fail_unless(ret_val.i == 2); + + ret = hash_delete(htable, &key); + fail_unless(ret == 0); + + /* Delete again with the same key */ + ret = hash_delete(htable, &key); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + + ret = hash_destroy(htable); + fail_unless(ret == 0); +} +END_TEST + +START_TEST(test_key_ulong) +{ + hash_table_t *htable; + int ret; + hash_value_t ret_val; + hash_value_t enter_val1; + hash_value_t enter_val2; + hash_key_t key; + + enter_val1.type = HASH_VALUE_INT; + enter_val1.i = 1; + enter_val2.type = HASH_VALUE_INT; + enter_val2.i = 2; + key.type = HASH_KEY_ULONG; + key.ul = 68ul; + + ret = hash_create(HTABLE_SIZE, &htable, NULL, NULL); + fail_unless(ret == 0); + + /* The table is empty, lookup should return error */ + ret = hash_lookup(htable, &key, &ret_val); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + /* Deleting with non-existing key should return error */ + ret = hash_delete(htable, &key); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + ret = hash_enter(htable, &key, &enter_val1); + fail_unless(ret == 0); + + hash_lookup(htable, &key, &ret_val); + fail_unless(ret == 0); + fail_unless(ret_val.i == 1); + + /* Overwrite the entry */ + ret = hash_enter(htable, &key, &enter_val2); + fail_unless(ret == 0); + + hash_lookup(htable, &key, &ret_val); + fail_unless(ret == 0); + fail_unless(ret_val.i == 2); + + ret = hash_delete(htable, &key); + fail_unless(ret == 0); + + /* Delete again with the same key */ + ret = hash_delete(htable, &key); + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND); + + ret = hash_destroy(htable); + fail_unless(ret == 0); +} +END_TEST + +static Suite *dhash_suite(void) +{ + Suite *s = suite_create(""); + + TCase *tc_basic = tcase_create("dhash API tests"); + tcase_add_test(tc_basic, test_key_const_string); + tcase_add_test(tc_basic, test_key_string); + tcase_add_test(tc_basic, test_key_ulong); + suite_add_tcase(s, tc_basic); + + return s; +} + +int main(void) +{ + int number_failed; + + Suite *s = dhash_suite(); + SRunner *sr = srunner_create(s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/sanity/ding-libs-tests.sh b/tests/sanity/ding-libs-tests.sh new file mode 100755 index 0000000..e3c3bd6 --- /dev/null +++ b/tests/sanity/ding-libs-tests.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +make run diff --git a/tests/sanity/ini_augment_ut_check.c b/tests/sanity/ini_augment_ut_check.c new file mode 100644 index 0000000..99dfd7f --- /dev/null +++ b/tests/sanity/ini_augment_ut_check.c @@ -0,0 +1,341 @@ +/* + INI LIBRARY + + Check based unit test for ini_config_augment. + + Copyright (C) Alexander Scheel 2017 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#include +#include +#include +#include + +/* #define TRACE_LEVEL 7 */ +#define TRACE_HOME +#include "trace.h" +#include "ini_configobj.h" +//#include "ini_config_priv.h" + +static int write_to_file(char *path, char *text) +{ + FILE *f = fopen(path, "w"); + int bytes = 0; + if (f == NULL) + return 1; + + bytes = fprintf(f, "%s", text); + if (bytes != strlen(text)) { + return 1; + } + + return fclose(f); +} + +static int exists_array(const char *needle, char **haystack, uint32_t count) +{ + uint32_t i = 0; + + for (i = 0; i < count; i++) { + fprintf(stderr, "%s == %s?\n", needle, haystack[i]); + if (strcmp(needle, haystack[i]) == 0) { + return 1; + } + } + + return 0; +} + +START_TEST(test_ini_augment_merge_sections) +{ + char base_path[PATH_MAX]; + char augment_path[PATH_MAX]; + + char config_base[] = + "[section]\n" + "key1 = first\n" + "key2 = exists\n"; + + char config_augment[] = + "[section]\n" + "key1 = augment\n" + "key3 = exists\n"; + + const char *builddir; + + uint32_t flags[3] = { INI_MS_DETECT , INI_MS_DETECT | INI_MS_PRESERVE, + INI_MS_DETECT | INI_MS_OVERWRITE }; + + int expected_attributes_counts[3] = { 3, 2, 2 }; + const char *test_sections[3] = { "section", "section", "section" }; + const char *test_attributes[3] = { "key3", "key1", "key1" }; + const char *test_attribute_values[3] = {"exists", "first", "augment" }; + + int ret; + int iter; + + builddir = getenv("builddir"); + if (builddir == NULL) { + builddir = "."; + } + + snprintf(base_path, PATH_MAX, "%s/tmp_augment_base.conf", builddir); + snprintf(augment_path, PATH_MAX, "%s/tmp_augment_augment.conf", builddir); + + ret = write_to_file(base_path, config_base); + fail_unless(ret == 0, "Failed to write %s: ret %d.\n", base_path, ret); + + write_to_file(augment_path, config_augment); + fail_unless(ret == 0, "Failed to write %s: ret %d.\n", augment_path, ret); + + for (iter = 0; iter < 3; iter++) { + uint32_t merge_flags = flags[iter]; + int expected_attributes_count = expected_attributes_counts[iter]; + const char *test_section = test_sections[iter]; + const char *test_attribute = test_attributes[iter]; + const char *test_attribute_value = test_attribute_values[iter]; + struct ini_cfgobj *in_cfg; + struct ini_cfgobj *result_cfg; + struct ini_cfgfile *file_ctx; + struct ref_array *error_list; + struct ref_array *success_list; + + char **sections; + int sections_count; + + char **attributes; + int attributes_count; + + struct value_obj *val; + char *val_str; + + /* Match only augment.conf */ + const char *m_patterns[] = { "^tmp_augment_augment.conf$", NULL }; + + /* Match all sections */ + const char *m_sections[] = { ".*", NULL }; + + /* Create config collection */ + ret = ini_config_create(&in_cfg); + fail_unless(ret == EOK, "Failed to create collection. Error %d\n", + ret); + + /* Open base.conf */ + ret = ini_config_file_open(base_path, 0, &file_ctx); + fail_unless(ret == EOK, "Failed to open file. Error %d\n", ret); + + /* Seed in_cfg with base.conf */ + ret = ini_config_parse(file_ctx, 1, 0, 0, in_cfg); + fail_unless(ret == EOK, "Failed to parse file context. Error %d\n", + ret); + + /* Update base.conf with augment.conf */ + ret = ini_config_augment(in_cfg, + builddir, + m_patterns, + m_sections, + NULL, + INI_STOP_ON_NONE, + 0, + INI_PARSE_NOSPACE|INI_PARSE_NOTAB, + merge_flags, + &result_cfg, + &error_list, + &success_list); + /* We always expect EEXIST due to DETECT being set. */ + fail_unless(ret == EEXIST, + "Failed to augment context. Error %d\n", ret); + + if (result_cfg) { + ini_config_destroy(in_cfg); + in_cfg = result_cfg; + result_cfg = NULL; + } + + /* Get a list of sections from the resulting cfg. */ + sections = ini_get_section_list(in_cfg, §ions_count, &ret); + fail_unless(ret == EOK, "Failed to get section list. Error %d\n", ret); + + /* Validate that the tested section exists. */ + ret = exists_array(test_section, sections, sections_count); + fail_if(ret == 0, "Failed to find expected section.\n"); + + /* Get a list of attributes from the resulting cfg. */ + attributes = ini_get_attribute_list(in_cfg, test_section, + &attributes_count, + &ret); + fail_unless(ret == EOK, "Failed to get attribute list. Error %d\n", + ret); + + /* Validate that the expected number of attributes exist. This + * distinguishes MERGE from PRESERVE/OVERWRITE. */ + fail_unless(expected_attributes_count == attributes_count, + "Expected %d attributes, but received %d.\n", + expected_attributes_count, attributes_count); + + /* Validate that the test attribute exists. This distinguishes + * PRESERVE from OVERWRITE. */ + ret = exists_array(test_attribute, attributes, attributes_count); + fail_if(ret == 0, "Failed to find expected attribute.\n"); + + ret = ini_get_config_valueobj(test_section, test_attribute, in_cfg, + 0, &val); + fail_unless(ret == EOK, "Failed to load value object. Error %d\n", + ret); + + val_str = ini_get_string_config_value(val, &ret); + fail_unless(ret == EOK, "Failed to get config value. Error %d\n", ret); + + /* Validate the value of the test attribute. */ + ret = strcmp(val_str, test_attribute_value); + + fail_unless(ret == 0, "Attribute %s didn't have expected value of " + "(%s): saw %s\n", test_attribute, test_attribute_value, + val_str); + + /* Cleanup */ + free(val_str); + ini_free_attribute_list(attributes); + ini_free_section_list(sections); + ref_array_destroy(error_list); + ini_config_file_destroy(file_ctx); + ref_array_destroy(success_list); + ini_config_destroy(in_cfg); + ini_config_destroy(result_cfg); + } + + remove(base_path); + remove(augment_path); +} +END_TEST + +START_TEST(test_ini_augment_empty_dir) +{ + int ret; + struct ini_cfgobj *ini_cfg; + struct ini_cfgfile *file_ctx; + struct value_obj *vo; + const char *patterns[] = { ".*", NULL }; + const char *sections[] = { ".*", NULL }; + char **section_list; + char **attrs_list; + struct ini_cfgobj *result_cfg = NULL; + int size; + char empty_dir_path[PATH_MAX] = {0}; + const char *builddir; + int32_t val; + char base_cfg[] = + "[section_one]\n" + "one = 1\n"; + + builddir = getenv("builddir"); + if (builddir == NULL) { + builddir = "."; + } + + ret = snprintf(empty_dir_path, PATH_MAX, "%s/tmp_empty_dir", builddir); + fail_if(ret > PATH_MAX || ret < 0, "snprintf failed\n"); + + ret = ini_config_file_from_mem(base_cfg, strlen(base_cfg), + &file_ctx); + fail_unless(ret == EOK, "Failed to load config. Error %d.\n", ret); + + ret = ini_config_create(&ini_cfg); + fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret); + ret = ini_config_parse(file_ctx, INI_STOP_ON_ERROR, INI_MV1S_ALLOW, 0, + ini_cfg); + fail_unless(ret == EOK, "Failed to parse configuration. Error %d.\n", ret); + + /* Create an empty directory */ + ret = mkdir(empty_dir_path, 0700); + if (ret == -1) { + ret = errno; + fail_if(ret != EEXIST, + "Failed to create empty directory. Error %d.\n", errno); + } + + ret = ini_config_augment(ini_cfg, + empty_dir_path, + patterns, + sections, + NULL, + INI_STOP_ON_ANY, + INI_MV1S_OVERWRITE, + INI_PARSE_NOWRAP, + INI_MV2S_OVERWRITE, + &result_cfg, + NULL, + NULL); + + fail_unless(ret == EOK); + + /* If the snippet directory is empty, result_cfg should be the original + * ini_cfg and not NULL */ + fail_if(result_cfg == NULL); + + /* Now check if the content of result_cfg is what we expected */ + section_list = ini_get_section_list(result_cfg, &size, NULL); + fail_unless(size == 1); + fail_unless(strcmp(section_list[0], "section_one") == 0); + + attrs_list = ini_get_attribute_list(result_cfg, section_list[0], + &size, NULL); + fail_unless(size == 1); + fail_unless(strcmp(attrs_list[0], "one") == 0); + + ret = ini_get_config_valueobj(section_list[0], + attrs_list[0], + result_cfg, + INI_GET_FIRST_VALUE, + &vo); + fail_unless(ret == 0); + + val = ini_get_int32_config_value(vo, 1, 100, NULL); + fail_unless(val == 1, "Expected attribute value not found.\n"); + + ini_free_attribute_list(attrs_list); + ini_free_section_list(section_list); + ini_config_destroy(result_cfg); + ini_config_destroy(ini_cfg); + ini_config_file_destroy(file_ctx); + remove(empty_dir_path); +} +END_TEST + +static Suite *ini_augment_suite(void) +{ + Suite *s = suite_create("ini_augment_suite"); + + TCase *tc_augment = tcase_create("ini_augment"); + tcase_add_test(tc_augment, test_ini_augment_merge_sections); + tcase_add_test(tc_augment, test_ini_augment_empty_dir); + + suite_add_tcase(s, tc_augment); + + return s; +} + +int main(void) +{ + int number_failed; + + Suite *s = ini_augment_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/sanity/ini_comment_ut.c b/tests/sanity/ini_comment_ut.c new file mode 100644 index 0000000..c248160 --- /dev/null +++ b/tests/sanity/ini_comment_ut.c @@ -0,0 +1,428 @@ +/* + INI LIBRARY + + Unit test for the comment object. + + Copyright (C) Dmitri Pal 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#include +#include +#include +#define TRACE_HOME +#include "trace.h" +#include "ini_comment.h" + +int verbose = 0; + +#define INIOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + +typedef int (*test_fn)(void); + +static int file_test(void) +{ + int error = EOK; + struct ini_comment *ic = NULL; + + if ((error = ini_comment_create(&ic)) || + (error = ini_comment_build(ic, ";Line 0")) || + (error = ini_comment_build(ic, ";Line 1")) || + (error = ini_comment_build(ic, ";Line 2"))) { + printf("Failed to create comment object %d\n", + error); + ini_comment_destroy(ic); + return error; + } + + INIOUT(printf("<==== Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + ini_comment_destroy(ic); + + return error; +} + +static int construct_test(void) +{ + int error = EOK; + struct ini_comment *ic = NULL; + const char *comments[] = { ";Line1", ";Line2", ";Line3", NULL }; + + error = ini_comment_construct(comments, + 0, + &ic); + if (error) { + printf("Failed to create comment object %d\n", + error); + return error; + } + + INIOUT(printf("<==== Constructed Comment 1 ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + ini_comment_destroy(ic); + + error = ini_comment_construct(comments, + 2, + &ic); + if (error) { + printf("Failed to create comment object %d\n", + error); + return error; + } + + INIOUT(printf("<==== Constructed Comment 2 ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + ini_comment_destroy(ic); + + return error; +} + + +static int alter_test(void) +{ + int error = EOK; + struct ini_comment *ic = NULL; + uint32_t i, num = 0; + char *line = NULL; + const char *expected[] = { ";Line 0 inserted", + ";Line 1 inserted", + "", + "", + ";Line 3 replaced", + "", + ";Line 4" }; + + + if ((error = ini_comment_create(&ic)) || + (error = ini_comment_build(ic, ";Line 0")) || + (error = ini_comment_build(ic, NULL)) || + (error = ini_comment_build(ic, ";Line 2")) || + (error = ini_comment_build(ic, ";Line 3")) || + (error = ini_comment_build(ic, ""))) { + printf("Failed to create comment object\n"); + ini_comment_destroy(ic); + return error; + } + + INIOUT(printf("<==== Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + + if ((error = ini_comment_append(ic, ";Line 4")) || + (error = ini_comment_clear(ic, 2)) || + (error = ini_comment_replace(ic, 3, ";Line 3 replaced")) || + (error = ini_comment_remove(ic, 0)) || + (error = ini_comment_insert(ic, 0, ";Line 0 inserted")) || + (error = ini_comment_insert(ic, 1, ";Line 1 inserted"))) { + printf("Failed to create comment object\n"); + ini_comment_destroy(ic); + return error; + } + + INIOUT(printf("<==== Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + + error = ini_comment_get_numlines(ic, &num); + if (error) { + printf("Failed to get number of lines.\n"); + ini_comment_destroy(ic); + return error; + } + + for (i = 0; i < num; i++) { + error = ini_comment_get_line(ic, i, &line, NULL); + if (error) { + printf("Failed to get line.\n"); + ini_comment_destroy(ic); + return error; + } + if (strcmp(line, expected[i]) != 0) { + printf("Lines do not match.\n"); + printf("GOT: %s\n", line); + printf("EXP: %s\n", expected[i]); + ini_comment_destroy(ic); + return error; + } + } + + INIOUT(printf("\n\nSwap test\n\n")); + + if ((error = ini_comment_swap(ic, 0 , 6)) || + (error = ini_comment_swap(ic, 1 , 5)) || + (error = ini_comment_swap(ic, 2 , 4))) { + printf("Failed to swap lines.\n"); + ini_comment_destroy(ic); + return error; + } + + for (i = 0; i < num; i++) { + error = ini_comment_get_line(ic, i, &line, NULL); + if (error) { + printf("Failed to get line.\n"); + ini_comment_destroy(ic); + return error; + } + if (strcmp(line, expected[6 - i]) != 0) { + printf("Lines do not match.\n"); + printf("GOT: %s\n", line); + printf("EXP: %s\n", expected[6 - i]); + ini_comment_destroy(ic); + return error; + } + } + + ini_comment_destroy(ic); + return error; +} + +static int copy_test(void) +{ + int error = EOK; + struct ini_comment *ic = NULL; + struct ini_comment *ic_copy = NULL; + char *line = NULL; + char *line_copy = NULL; + uint32_t i, num = 0; + + INIOUT(printf("\n\nCopy test\n\n")); + + if ((error = ini_comment_create(&ic)) || + (error = ini_comment_build(ic, ";Line 0")) || + (error = ini_comment_build(ic, ";Line 1")) || + (error = ini_comment_build(ic, ";Line 2"))) { + printf("Failed to create comment object %d\n", + error); + ini_comment_destroy(ic); + return error; + } + + INIOUT(printf("<==== Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + + if ((error = ini_comment_copy(ic, &ic_copy))) { + printf("Failed to create comment object %d\n", + error); + ini_comment_destroy(ic); + return error; + } + + INIOUT(printf("<==== Comment Copy====>\n")); + INIOUT(ini_comment_print(ic_copy, stdout)); + INIOUT(printf("<=================>\n")); + + error = ini_comment_get_numlines(ic, &num); + if (error) { + printf("Failed to get number of lines.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_copy); + return error; + } + + for (i = 0; i < num; i++) { + error = ini_comment_get_line(ic, i, &line, NULL); + if (error) { + printf("Failed to get line.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_copy); + return error; + } + error = ini_comment_get_line(ic_copy, i, &line_copy, NULL); + if (error) { + printf("Failed to get line.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_copy); + return error; + } + if (strcmp(line, line_copy) != 0) { + printf("Lines do not match.\n"); + printf("Source: %s\n", line); + printf("Copy: %s\n", line_copy); + ini_comment_destroy(ic); + ini_comment_destroy(ic_copy); + return -1; + } + } + + ini_comment_destroy(ic); + ini_comment_destroy(ic_copy); + + return error; +} + +static int add_test(void) +{ + int error = EOK; + struct ini_comment *ic = NULL; + struct ini_comment *ic_to_add = NULL; + struct ini_comment *ic_cmp = NULL; + uint32_t i, num1 = 0, num2 = 0; + char *line1 = NULL; + char *line2 = NULL; + + + INIOUT(printf("\n\nAdd test\n\n")); + + if ((error = ini_comment_create(&ic)) || + (error = ini_comment_build(ic, ";Line 0")) || + (error = ini_comment_build(ic, ";Line 1")) || + (error = ini_comment_build(ic, ";Line 2"))) { + printf("Failed to create comment object %d\n", + error); + ini_comment_destroy(ic); + return error; + } + + INIOUT(printf("<==== Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + + if ((error = ini_comment_create(&ic_to_add)) || + (error = ini_comment_build(ic_to_add, ";Line 3")) || + (error = ini_comment_build(ic_to_add, ";Line 4")) || + (error = ini_comment_build(ic_to_add, ";Line 5"))) { + printf("Failed to create comment object %d\n", + error); + ini_comment_destroy(ic); + return error; + } + + INIOUT(printf("<==== Comment To Add ====>\n")); + INIOUT(ini_comment_print(ic_to_add, stdout)); + INIOUT(printf("<=================>\n")); + + error = ini_comment_add(ic_to_add, ic); + if (error) { + printf("Failed to add one comment to another.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_to_add); + return error; + } + + INIOUT(printf("<==== Merged Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + + if ((error = ini_comment_create(&ic_cmp)) || + (error = ini_comment_build(ic_cmp, ";Line 0")) || + (error = ini_comment_build(ic_cmp, ";Line 1")) || + (error = ini_comment_build(ic_cmp, ";Line 2")) || + (error = ini_comment_build(ic_cmp, ";Line 3")) || + (error = ini_comment_build(ic_cmp, ";Line 4")) || + (error = ini_comment_build(ic_cmp, ";Line 5"))) { + printf("Failed to create comment object %d\n", + error); + ini_comment_destroy(ic_cmp); + return error; + } + + ini_comment_destroy(ic_to_add); + + error = ini_comment_get_numlines(ic, &num1); + if (error) { + printf("Failed to get number of lines.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_cmp); + return error; + } + + error = ini_comment_get_numlines(ic, &num2); + if (error) { + printf("Failed to get number of lines.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_cmp); + return error; + } + + if (num1 != num2) { + printf("Sizes are different.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_cmp); + return -1; + } + + for (i = 0; i < num1; i++) { + line1 = NULL; + error = ini_comment_get_line(ic, i, &line1, NULL); + if (error) { + printf("Failed to get line.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_cmp); + return error; + } + line2 = NULL; + error = ini_comment_get_line(ic_cmp, i, &line2, NULL); + if (error) { + printf("Failed to get line.\n"); + ini_comment_destroy(ic); + ini_comment_destroy(ic_cmp); + return error; + } + if (strcmp(line1, line2) != 0) { + printf("Lines do not match.\n"); + printf("1st: %s\n", line1); + printf("2nd: %s\n", line2); + ini_comment_destroy(ic); + ini_comment_destroy(ic_cmp); + return -1; + } + } + + ini_comment_destroy(ic); + ini_comment_destroy(ic_cmp); + + return error; +} + + +int main(int argc, char *argv[]) +{ + int error = EOK; + test_fn tests[] = { file_test, + alter_test, + copy_test, + add_test, + construct_test, + NULL }; + test_fn t; + int i = 0; + char *var; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + else { + var = getenv("COMMON_TEST_VERBOSE"); + if (var) verbose = 1; + } + + INIOUT(printf("Start\n")); + + while ((t = tests[i++])) { + error = t(); + if (error) { + INIOUT(printf("Failed with error %d!\n", error)); + return error; + } + } + + INIOUT(printf("Success!\n")); + return 0; +} diff --git a/tests/sanity/ini_configmod_ut_check.c b/tests/sanity/ini_configmod_ut_check.c new file mode 100644 index 0000000..72a9a4e --- /dev/null +++ b/tests/sanity/ini_configmod_ut_check.c @@ -0,0 +1,1692 @@ +/* + INI LIBRARY + + Unit test for the configuration object modification API. + + Copyright (C) Lukas Slebodnik 2015 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#include +#include +#include +#include +#include + +/* #define TRACE_LEVEL 7 */ +#define TRACE_HOME +#include "trace.h" +#include "ini_configobj.h" +#include "collection_tools.h" +#include "ini_configmod.h" +#include "path_utils.h" +#include + +int verbose = 0; + +#define WRAP_SIZE 80 + +static void dump_configuration(struct ini_cfgobj *in_cfg, + FILE *file) +{ + int ret; + struct simplebuffer *sbobj = NULL; + void *buff; + uint32_t len; + + ret = simplebuffer_alloc(&sbobj); + fail_unless(ret == EOK, + "Failed to allocate buffer. Error %d.\n", ret); + + ret = ini_config_serialize(in_cfg, sbobj); + fail_unless(ret == EOK, + "Failed to serialize. Error %d.\n", ret); + + buff = simplebuffer_get_vbuf(sbobj); + len = simplebuffer_get_len(sbobj); + ret = fwrite(buff, 1, len, file); + fail_if(ret == -1, + "Failed to write to file. Error: %d %s\n", ret, strerror(ret)); + + simplebuffer_free(sbobj); + return; +} + +static int call_diff(const char *function, + const char *expected_cfg, + size_t expected_cfg_len, + const char *res_cfg, + size_t res_cfg_len) +{ + char expected_fn[PATH_MAX]; + char res_fn[PATH_MAX]; + char command[PATH_MAX * 3]; + char *builddir; + int ret; + int expected_fd; + int res_fd; + + builddir = getenv("builddir"); + + snprintf(expected_fn, PATH_MAX, "%s/expected.conf_%s_XXXXXX", + (builddir == NULL) ? "." : builddir, function); + snprintf(res_fn, PATH_MAX, "%s/result.conf_%s_XXXXXX", + (builddir == NULL) ? "." : builddir, function); + + expected_fd = mkstemp(expected_fn); + fail_if(expected_fd == -1, "mkstemp failed: %s\n", strerror(errno)); + + ret = write(expected_fd, expected_cfg, expected_cfg_len); + fail_if(ret == -1, + "Failed write to %s. Error %s\n", + expected_fn, strerror(errno)); + + close(expected_fd); + + res_fd = mkstemp(res_fn); + fail_if(res_fd == -1, "mkstemp failed: %s\n", strerror(errno)); + + ret = write(res_fd, res_cfg, res_cfg_len); + fail_if(ret == -1, + "Failed write to %s. Error %s\n", + expected_fn, strerror(errno)); + close(res_fd); + + snprintf(command, PATH_MAX * 3, "diff -wi %s %s", expected_fn, res_fn); + ret = system(command); + fail_if(ret == -1, + "Failed to execute command:%s. Erorr %s\n", + command, strerror(errno)); + + return EOK; +} + +#define assert_configuration_equal(expected_cfg, expected_cfg_len, res_cfg) \ + _assert_configuration_equal(expected_cfg, expected_cfg_len, res_cfg, \ + __func__, __FILE__, __LINE__) +static void _assert_configuration_equal(const char *expected_cfg, + size_t expected_cfg_len, + struct ini_cfgobj *res_cfg, + const char *function, + const char *file, + int line) +{ + char *res_buffer = NULL; + size_t res_buffer_size; + FILE *f_memstream; + int ret; + + --expected_cfg_len; /* do not use trailing zero */ + + f_memstream = open_memstream(&res_buffer, &res_buffer_size); + fail_if(f_memstream == NULL, + "\n\t[%s:%d] open_memstream failed.", file, line); + + dump_configuration(res_cfg, f_memstream); + fclose(f_memstream); + + fail_unless(expected_cfg_len == res_buffer_size, + "\n\t[%s:%d] Size of expected config %zu and result config %d " + "does not match. Res:%d\n", + file, line, expected_cfg_len, res_buffer_size, + call_diff(function, expected_cfg, expected_cfg_len, + res_buffer, res_buffer_size)); + + ret = memcmp(res_buffer, expected_cfg, expected_cfg_len); + fail_unless(ret == EOK, + "\n\t[%s:%d] Configurations are not identical. Res:%d\n", + file, line, + call_diff(function, expected_cfg, expected_cfg_len, + res_buffer, res_buffer_size)); + + free(res_buffer); +} + +static struct ini_cfgobj *get_ini_config_from_str(char input_data[], + size_t input_data_len) +{ + struct ini_cfgobj *in_cfg; + struct ini_cfgfile *file_ctx; + int ret; + + ret = ini_config_create(&in_cfg); + fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret); + + ret = ini_config_file_from_mem(input_data, input_data_len, &file_ctx); + fail_unless(ret == EOK, "Failed to load config. Error %d.\n", ret); + + ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, INI_MV1S_ALLOW, 0, + in_cfg); + fail_unless(ret == EOK, "Failed to parse config. Error %d.\n", ret); + + ini_config_file_destroy(file_ctx); + + return in_cfg; +} + +START_TEST(test_delete_value_wrong_arguments) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg = NULL; + + char exp_data[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n"; + + in_cfg = get_ini_config_from_str(exp_data, sizeof(exp_data)); + assert_configuration_equal(exp_data, sizeof(exp_data), in_cfg); + + /* missing ini_config */ + ret = ini_config_delete_value(NULL, "one", COL_DSP_NDUP, "key1", 0); + fail_unless(ret == EINVAL, "delete value should fail. Error: %d", ret); + assert_configuration_equal(exp_data, sizeof(exp_data), in_cfg); + + /* missing section */ + ret = ini_config_delete_value(in_cfg, NULL, COL_DSP_NDUP, "key1", 0); + fail_unless(ret == EINVAL, "delete value should fail. Error: %d", ret); + assert_configuration_equal(exp_data, sizeof(exp_data), in_cfg); + + /* missing key */ + ret = ini_config_delete_value(in_cfg, "one", COL_DSP_NDUP, NULL, 0); + fail_unless(ret == EINVAL, "delete value should fail. Error: %d", ret); + assert_configuration_equal(exp_data, sizeof(exp_data), in_cfg); + + /* value index is too low */ + ret = ini_config_delete_value(in_cfg, "one", COL_DSP_NDUP, "key1", -1); + fail_unless(ret == EINVAL, "delete value should fail. Error: %d", ret); + assert_configuration_equal(exp_data, sizeof(exp_data), in_cfg); + + /* value index is too high */ + ret = ini_config_delete_value(in_cfg, "one", COL_DSP_NDUP, "key1", 1); + fail_unless(ret == ENOENT, "delete value should fail. Error: %d", ret); + assert_configuration_equal(exp_data, sizeof(exp_data), in_cfg); + + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_delete_value) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key1 = first\n" + "key1 = second\n" + "key1 = third\n" + "key1 = last\n"; + + char delete_first[] = + "[zero]\n" + "[one]\n" + "key1 = first\n" + "key1 = second\n" + "key1 = third\n"; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + ret = ini_config_delete_value(in_cfg, "one", COL_DSP_NDUP, "key1", 3); + fail_unless(ret == EOK, "delete value should fail. Error: %d", ret); + assert_configuration_equal(delete_first, sizeof(delete_first), in_cfg); + + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_update_comments_wrong_arguments) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[one]\n" + "key1 = value1\n" + "key1 = value1a\n" + "key1 = value1a_bis\n" + "// This is a test\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key2 = value2\n" + "key3 = value3\n"; + + const char *comment[] = { "// This is a test", NULL }; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* missing ini_config */ + ret = ini_config_update_comment(NULL, "one", "key1", comment, 1, 3); + fail_unless(ret == EINVAL, "update commants should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* missing section */ + ret = ini_config_update_comment(in_cfg, NULL, "key1", comment, 1, 3); + fail_unless(ret == EINVAL, "update commants should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* missing key */ + ret = ini_config_update_comment(in_cfg, "one", NULL, comment, 1, 3); + fail_unless(ret == EINVAL, "update commants should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* wrong section */ + ret = ini_config_update_comment(in_cfg, "noexist", "key1", comment, 1, 3); + fail_unless(ret == ENOENT, "update commants should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* wrong key */ + ret = ini_config_update_comment(in_cfg, "one", "noexist", comment, 1, 3); + fail_unless(ret == ENOENT, "update commants should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* value index is too low */ + ret = ini_config_update_comment(in_cfg, "one", "key1", comment, 1, -1); + fail_unless(ret == EINVAL, "update commants should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* value index is too high */ + ret = ini_config_update_comment(in_cfg, "one", "key1", comment, 1, 5); + fail_unless(ret == ENOENT, "update commants should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_update_comments) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[one]\n" + "key1 = value1\n" + "key1 = value1a\n" + "key1 = value1a_bis\n" + "key1 = value1b\n" + "// this is a comment\n" + "key1 = value1c\n" + "key2 = value2\n" + "key3 = value3\n"; + + char exp_data_1comment[] = + "[one]\n" + "// This is a test1\n" + "key1 = value1\n" + "key1 = value1a\n" + "key1 = value1a_bis\n" + "key1 = value1b\n" + "// this is a comment\n" + "key1 = value1c\n" + "key2 = value2\n" + "key3 = value3\n"; + + char exp_data_2comments[] = + "[one]\n" + "// This is a test1\n" + "// This is a test2\n" + "key1 = value1\n" + "key1 = value1a\n" + "key1 = value1a_bis\n" + "key1 = value1b\n" + "// this is a comment\n" + "key1 = value1c\n" + "key2 = value2\n" + "key3 = value3\n"; + + char exp_data_1comment_after2[] = + "[one]\n" + "key1 = value1\n" + "key1 = value1a\n" + "// This is a test1\n" + "key1 = value1a_bis\n" + "key1 = value1b\n" + "// this is a comment\n" + "key1 = value1c\n" + "key2 = value2\n" + "key3 = value3\n"; + + char exp_replaced[] = + "[one]\n" + "key1 = value1\n" + "key1 = value1a\n" + "key1 = value1a_bis\n" + "key1 = value1b\n" + "// This is a test1\n" + "// This is a test2\n" + "key1 = value1c\n" + "key2 = value2\n" + "key3 = value3\n"; + + char exp_removed_comment[] = + "[one]\n" + "key1 = value1\n" + "key1 = value1a\n" + "key1 = value1a_bis\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key2 = value2\n" + "key3 = value3\n"; + + const char *comments[] = { "// This is a test1", "// This is a test2", + NULL }; + + const char *empty_comment[] = { NULL }; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add comments with size */ + ret = ini_config_update_comment(in_cfg, "one", "key1", comments, 1, 0); + fail_unless(ret == EOK, "update commants should fail. Error: %d", ret); + assert_configuration_equal(exp_data_1comment, sizeof(exp_data_1comment), + in_cfg); + ini_config_destroy(in_cfg); + + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add comments with size */ + ret = ini_config_update_comment(in_cfg, "one", "key1", comments, 2, 0); + fail_unless(ret == EOK, "update commants should fail. Error: %d", ret); + assert_configuration_equal(exp_data_2comments, sizeof(exp_data_2comments), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add comments (NULL terminated array), size is 0 */ + ret = ini_config_update_comment(in_cfg, "one", "key1", comments, 0, 0); + fail_unless(ret == EOK, "update commants should fail. Error: %d", ret); + assert_configuration_equal(exp_data_2comments, sizeof(exp_data_2comments), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add comments (NULL terminated array), size is 0 */ + ret = ini_config_update_comment(in_cfg, "one", "key1", comments, 1, 2); + fail_unless(ret == EOK, "update commants should fail. Error: %d", ret); + assert_configuration_equal(exp_data_1comment_after2, + sizeof(exp_data_1comment_after2), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* replace comment */ + ret = ini_config_update_comment(in_cfg, "one", "key1", comments, 0, 4); + fail_unless(ret == EOK, "update commants should fail. Error: %d", ret); + assert_configuration_equal(exp_replaced, sizeof(exp_replaced), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* remove comment */ + ret = ini_config_update_comment(in_cfg, "one", "key1", + empty_comment, 0, 4); + fail_unless(ret == EOK, "update commants should fail. Error: %d", ret); + assert_configuration_equal(exp_removed_comment, + sizeof(exp_removed_comment), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* remove comment (2nd way; argument is NULL) */ + ret = ini_config_update_comment(in_cfg, "one", "key1", + NULL, 0, 4); + fail_unless(ret == EOK, "update commants should fail. Error: %d", ret); + assert_configuration_equal(exp_removed_comment, + sizeof(exp_removed_comment), + in_cfg); + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_add_str_wrong_arguments) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n"; + + const char *comments[] = { "// This is a test1", "// This is a test2", + NULL }; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* missing ini_config */ + ret = ini_config_add_str_value(NULL, "one", "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_END, "key1", + 0, INI_VA_NOCHECK); + fail_unless(ret == EINVAL, "Add str should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* missing section */ + ret = ini_config_add_str_value(in_cfg, NULL, "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_END, "key1", + 0, INI_VA_NOCHECK); + fail_unless(ret == EINVAL, "Add str should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* missing key */ + ret = ini_config_add_str_value(in_cfg, "one", NULL, "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_END, "key1", + 0, INI_VA_NOCHECK); + fail_unless(ret == EINVAL, "Add str should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* missing value */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", NULL, + comments, 2, WRAP_SIZE, COL_DSP_END, "key1", + 0, INI_VA_NOCHECK); + fail_unless(ret == EINVAL, "Add str should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* wrong index */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_END, "key1", + -1, INI_VA_NOCHECK); + fail_unless(ret == EINVAL, "Add str should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* wrong flag */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_END, "key1", + 0, 0xff); + fail_unless(ret == ENOSYS, "Add str should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add duplicate for missing key */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, + "key1", 0, INI_VA_NOCHECK); + fail_unless(ret == ENOENT, "Add str should fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_add_str_simple) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + const char *comments[] = { "// This is a test1", "// This is a test2", + NULL }; + + char add_new_value_to_end[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n" + "newkey = newvalue\n"; + + char add_new_value_to_end_with_comment[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n" + "// This is a test1\n" + "// This is a test2\n" + "newkey = newvalue\n"; + + char add_new_value_to_front[] = + "[zero]\n" + "[one]\n" + "newkey = newvalue\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + char add_new_value_to_front_with_comment[] = + "[zero]\n" + "[one]\n" + "// This is a test1\n" + "// This is a test2\n" + "newkey = newvalue\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + char add_new_value_after_key1[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "newkey = newvalue\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + char add_new_value_before_key2[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "newkey = newvalue\n" + "key2 = value2a\n"; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Simple add new value to end of section */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_END, NULL, + 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_to_end, + sizeof(add_new_value_to_end), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value with comment to end of section */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_END, NULL, + 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_to_end_with_comment, + sizeof(add_new_value_to_end_with_comment), + in_cfg); + ini_config_destroy(in_cfg); + + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Simple add new value to the begin of section */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FRONT, NULL, + 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_to_front, + sizeof(add_new_value_to_front), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value with comment to the begin of section */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_FRONT, NULL, + 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_to_front_with_comment, + sizeof(add_new_value_to_front_with_comment), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value after "key1" with index 0 */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_AFTER, "key1", + 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_after_key1, + sizeof(add_new_value_after_key1), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value after "key1" with index 1 (index ignored) */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_AFTER, "key1", + 1, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_after_key1, + sizeof(add_new_value_after_key1), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value after "key1" with very big index (index ignored) */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_AFTER, "key1", + 1000, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_after_key1, + sizeof(add_new_value_after_key1), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value before "key2" */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_BEFORE, "key2", + 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_before_key2, + sizeof(add_new_value_before_key2), + in_cfg); + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_add_str_duplicate) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "key1 = value1a\n" + "key1 = value1b\n" + "// This is a test1\n" + "key2 = value2a\n"; + + char add_first_duplicate[] = + "[zero]\n" + "[one]\n" + "key0 = newvalue\n" + "key0 = value0a\n" + "key1 = value1a\n" + "key1 = value1b\n" + "// This is a test1\n" + "key2 = value2a\n"; + + char add_first_duplicate_for_multi[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "key1 = newvalue\n" + "key1 = value1a\n" + "key1 = value1b\n" + "// This is a test1\n" + "key2 = value2a\n"; + + char add_last_duplicate[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key1 = newvalue\n" + "// This is a test1\n" + "key2 = value2a\n"; + + char add_duplicate_with_index1[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "key1 = value1a\n" + "// This is a test1\n" + "// This is a test2\n" + "key1 = newvalue\n" + "key1 = value1b\n" + "// This is a test1\n" + "key2 = value2a\n"; + + const char *comments[] = { "// This is a test1", "// This is a test2", + NULL }; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* cannot add key as the first duplicate for non-existing key */ + ret = ini_config_add_str_value(in_cfg, "one", "noexist", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_NOCHECK); + fail_unless(ret == ENOENT, "Add duplicate key must fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* cannot add key as the last duplicate for non-existing key */ + ret = ini_config_add_str_value(in_cfg, "one", "noexist", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_LASTDUP, + NULL, 0, INI_VA_NOCHECK); + fail_unless(ret == ENOENT, "Add duplicate key must fail. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate value */ + ret = ini_config_add_str_value(in_cfg, "one", "key0", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_first_duplicate, + sizeof(add_first_duplicate), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate value with other_key (reference key is ignored) */ + ret = ini_config_add_str_value(in_cfg, "one", "key0", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + "key2", 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_first_duplicate, + sizeof(add_first_duplicate), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate value with index (index is ignored) */ + ret = ini_config_add_str_value(in_cfg, "one", "key0", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + "key0", 1, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_first_duplicate, + sizeof(add_first_duplicate), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate value with multiple keys */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_first_duplicate_for_multi, + sizeof(add_first_duplicate_for_multi), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate as last */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_LASTDUP, + NULL, 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_last_duplicate, + sizeof(add_last_duplicate), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate with index 0 (the same as COL_DSP_FIRSTDUP) */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + NULL, 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_first_duplicate_for_multi, + sizeof(add_first_duplicate_for_multi), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate with index and other_key (other_key is ignored) */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + "key0", 0, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_first_duplicate_for_multi, + sizeof(add_first_duplicate_for_multi), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate with big index (the same as COL_DSP_NDUP) */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + NULL, 100, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_last_duplicate, + sizeof(add_last_duplicate), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Add duplicate with index 1 */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + comments, 0, WRAP_SIZE, COL_DSP_NDUP, + NULL, 1, INI_VA_NOCHECK); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_duplicate_with_index1, + sizeof(add_duplicate_with_index1), in_cfg); + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_add_str_update_specific_value) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key0 = valuer0\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key1 = value1d\n" + "key2 = value2\n" + "key3 = value3\n"; + + const char *comments[] = { "// This is a test1", "// This is a test2", + NULL }; + + char modify_existing_value[] = + "[zero]\n" + "[one]\n" + "key0 = valuer0\n" + "// This is a test1\n" + "// This is a test2\n" + "key1 = newvalue\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key1 = value1d\n" + "key2 = value2\n" + "key3 = value3\n"; + + char modify_existing_value_index[] = + "[zero]\n" + "[one]\n" + "key0 = valuer0\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key1 = newvalue\n" + "key2 = value2\n" + "key3 = value3\n"; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* update of non-existing value fails */ + ret = ini_config_add_str_value(in_cfg, "one", "key4", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, NULL, + 0, INI_VA_MOD); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_MOD will not add key for non-existing value + * even with proper position flag. This is difference between + * INI_VA_MOD and INI_VA_MODADD */ + ret = ini_config_add_str_value(in_cfg, "one", "key1.x", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_BEFORE, "key2", + 0, INI_VA_MOD); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, + sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* strict flag + update of non-existing value fails */ + ret = ini_config_add_str_value(in_cfg, "one", "key4", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, NULL, + 0, INI_VA_MOD_E); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add will update existing value */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, NULL, + 0, INI_VA_MOD); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value, + sizeof(modify_existing_value), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* strict flag + add will update existing value */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, NULL, + 0, INI_VA_MOD_E); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value, + sizeof(modify_existing_value), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add will update existing value using index */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, NULL, + 3, INI_VA_MOD); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value_index, + sizeof(modify_existing_value_index), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* strict flag + add will update existing value using index */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, NULL, + 3, INI_VA_MOD_E); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value_index, + sizeof(modify_existing_value_index), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add will update existing value using big index */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, NULL, + 1000, INI_VA_MOD); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value_index, + sizeof(modify_existing_value_index), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* strict flag + add will NOT update existing value using big index */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, NULL, + 1000, INI_VA_MOD_E); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, + sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_add_str_update_MODADD) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key0 = valuer0\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key1 = value1d\n" + "key2 = value2\n" + "key3 = value3\n"; + + char modify_add_non_existing_value[] = + "[zero]\n" + "[one]\n" + "key0 = valuer0\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key1 = value1d\n" + "key1.x = newvalue\n" + "key2 = value2\n" + "key3 = value3\n"; + + const char *comments[] = { "// This is a test1", "// This is a test2", + NULL }; + + char modify_existing_value[] = + "[zero]\n" + "[one]\n" + "key0 = valuer0\n" + "// This is a test1\n" + "// This is a test2\n" + "key1 = newvalue\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key1 = value1d\n" + "key2 = value2\n" + "key3 = value3\n"; + + char modify_existing_value_index[] = + "[zero]\n" + "[one]\n" + "key0 = valuer0\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key1 = value1c\n" + "key1 = newvalue\n" + "key2 = value2\n" + "key3 = value3\n"; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* update of non-existing value fails with wrong position */ + ret = ini_config_add_str_value(in_cfg, "one", "key4", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, NULL, + 0, INI_VA_MODADD); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_MODADD will add key for non-existing value*/ + ret = ini_config_add_str_value(in_cfg, "one", "key1.x", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_BEFORE, "key2", + 0, INI_VA_MODADD); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_add_non_existing_value, + sizeof(modify_add_non_existing_value), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* strict flag + update of non-existing value fails */ + ret = ini_config_add_str_value(in_cfg, "one", "key4", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, NULL, + 0, INI_VA_MODADD_E); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add will update existing value */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, NULL, + 0, INI_VA_MODADD); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value, + sizeof(modify_existing_value), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* strict flag + add will update existing value */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_NDUP, NULL, + 0, INI_VA_MODADD_E); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value, + sizeof(modify_existing_value), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add will update existing value using index */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, NULL, + 3, INI_VA_MODADD); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value_index, + sizeof(modify_existing_value_index), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* strict flag + add will update existing value using index */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, NULL, + 3, INI_VA_MODADD_E); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value_index, + sizeof(modify_existing_value_index), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add will update existing value using big index */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, NULL, + 1000, INI_VA_MODADD); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(modify_existing_value_index, + sizeof(modify_existing_value_index), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* strict flag + add will NOT update existing value using big index */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, NULL, + 1000, INI_VA_MODADD_E); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, + sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); +} +END_TEST + +/* INI_VA_CLEAN does not have effect to operation without duplicit key */ +START_TEST(test_add_str_simple_clean) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + const char *comments[] = { "// This is a test1", "// This is a test2", + NULL }; + + char add_new_value_to_end[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n" + "newkey = newvalue\n"; + + char add_new_value_to_end_with_comment[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n" + "// This is a test1\n" + "// This is a test2\n" + "newkey = newvalue\n"; + + char add_new_value_to_front[] = + "[zero]\n" + "[one]\n" + "newkey = newvalue\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + char add_new_value_to_front_with_comment[] = + "[zero]\n" + "[one]\n" + "// This is a test1\n" + "// This is a test2\n" + "newkey = newvalue\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + char add_new_value_after_key1[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "newkey = newvalue\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + char add_new_value_before_key2[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "newkey = newvalue\n" + "key2 = value2a\n"; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Simple add new value to end of section */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_END, NULL, + 0, INI_VA_CLEAN); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_to_end, + sizeof(add_new_value_to_end), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value with comment to end of section */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_END, NULL, + 0, INI_VA_CLEAN); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_to_end_with_comment, + sizeof(add_new_value_to_end_with_comment), + in_cfg); + ini_config_destroy(in_cfg); + + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* Simple add new value to the begin of section */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FRONT, NULL, + 0, INI_VA_CLEAN); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_to_front, + sizeof(add_new_value_to_front), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value with comment to the begin of section */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + comments, 2, WRAP_SIZE, COL_DSP_FRONT, NULL, + 0, INI_VA_CLEAN); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_to_front_with_comment, + sizeof(add_new_value_to_front_with_comment), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value after "key1" with index 0 */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_AFTER, "key1", + 0, INI_VA_CLEAN); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_after_key1, + sizeof(add_new_value_after_key1), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value after "key1" with index 1 (index ignored) */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_AFTER, "key1", + 1, INI_VA_CLEAN); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_after_key1, + sizeof(add_new_value_after_key1), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value after "key1" with very big index (index ignored) */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_AFTER, "key1", + 1000, INI_VA_CLEAN); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_after_key1, + sizeof(add_new_value_after_key1), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* add new value before "key2" */ + ret = ini_config_add_str_value(in_cfg, "one", "newkey", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_BEFORE, "key2", + 0, INI_VA_CLEAN); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_new_value_before_key2, + sizeof(add_new_value_before_key2), + in_cfg); + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_add_str_duplicate_error) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key2 = value2a\n"; + + char add_non_existing_value[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "key1 = value1a\n" + "key1 = value1b\n" + "key1.x = newvalue\n" + "key2 = value2a\n"; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_DUPERROR will return EEXIST for duplicit key + * and COL_DSP_FIRSTDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key0", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_DUPERROR); + fail_unless(ret == EEXIST, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_DUPERROR will return EEXIST for duplicit key + * and COL_DSP_FIRSTDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_DUPERROR); + fail_unless(ret == EEXIST, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_DUPERROR will return EEXIST for duplicit key + * and COL_DSP_FIRSTDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key2", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_DUPERROR); + fail_unless(ret == EEXIST, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_DUPERROR will return EEXIST for duplicit key + * and COL_DSP_LASTDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_LASTDUP, + NULL, 0, INI_VA_DUPERROR); + fail_unless(ret == EEXIST, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_DUPERROR will return EEXIST for duplicit key + * and COL_DSP_NDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + NULL, 0, INI_VA_DUPERROR); + fail_unless(ret == EEXIST, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_DUPERROR will return EEXIST for duplicit key + * and COL_DSP_NDUP will return error due to nonexisting duplicate + * (index and other_key are ignored. + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + "key1", 1, INI_VA_DUPERROR); + fail_unless(ret == EEXIST, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_DUPERROR will return EEXIST for duplicit key + * and COL_DSP_NDUP will return error due to nonexisting duplicate + * big index is ignored. + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + NULL, 100, INI_VA_DUPERROR); + fail_unless(ret == EEXIST, "Failed to add str. Error: %d", ret); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* entry will be added with INI_VA_DUPERROR and non-existing key. */ + ret = ini_config_add_str_value(in_cfg, "one", "key1.x", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_BEFORE, "key2", + 0, INI_VA_DUPERROR); + fail_unless(ret == EOK, "Failed to add str. Error: %d", ret); + assert_configuration_equal(add_non_existing_value, + sizeof(add_non_existing_value), in_cfg); + ini_config_destroy(in_cfg); +} +END_TEST + +START_TEST(test_add_str_duplicate_clean) +{ + int ret = EOK; + struct ini_cfgobj *in_cfg; + + char input_data[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "key1 = value1a\n" + "key1 = value1b\n" + "// This is a test1\n" + "key2 = value2a\n"; + + char only_removed_key[] = + "[zero]\n" + "[one]\n" + "key1 = value1a\n" + "key1 = value1b\n" + "// This is a test1\n" + "key2 = value2a\n"; + + char only_removed_all_duplicates[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "// This is a test1\n" + "key2 = value2a\n"; + + char only_removed_key_with_comment[] = + "[zero]\n" + "[one]\n" + "key0 = value0a\n" + "key1 = value1a\n" + "key1 = value1b\n"; + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_CLEAN will remove duplicit key + * and COL_DSP_FIRSTDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key0", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_CLEAN); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(only_removed_key, sizeof(only_removed_key), + in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_CLEAN will remove app duplicit keys + * and COL_DSP_FIRSTDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_CLEAN); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(only_removed_all_duplicates, + sizeof(only_removed_all_duplicates), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_CLEAN will remove app duplicit keys + * and COL_DSP_FIRSTDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key2", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_FIRSTDUP, + NULL, 0, INI_VA_CLEAN); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(only_removed_key_with_comment, + sizeof(only_removed_key_with_comment), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_CLEAN will remove all duplicit keys + * and COL_DSP_LASTDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_LASTDUP, + NULL, 0, INI_VA_CLEAN); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(only_removed_all_duplicates, + sizeof(only_removed_all_duplicates), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_CLEAN will remove all duplicit keys + * and COL_DSP_NDUP will return error due to nonexisting duplicate + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + NULL, 0, INI_VA_CLEAN); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(only_removed_all_duplicates, + sizeof(only_removed_all_duplicates), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_CLEAN will remove all duplicit keys + * and COL_DSP_NDUP will return error due to nonexisting duplicate + * (index and other_key are ignored. + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + "key1", 1, INI_VA_CLEAN); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(only_removed_all_duplicates, + sizeof(only_removed_all_duplicates), in_cfg); + ini_config_destroy(in_cfg); + + in_cfg = get_ini_config_from_str(input_data, sizeof(input_data)); + assert_configuration_equal(input_data, sizeof(input_data), in_cfg); + + /* INI_VA_CLEAN will remove all duplicit keys + * and COL_DSP_NDUP will return error due to nonexisting duplicate + * big index is ignored. + */ + ret = ini_config_add_str_value(in_cfg, "one", "key1", "newvalue", + NULL, 0, WRAP_SIZE, COL_DSP_NDUP, + NULL, 100, INI_VA_CLEAN); + fail_unless(ret == ENOENT, "Failed to add str. Error: %d", ret); + assert_configuration_equal(only_removed_all_duplicates, + sizeof(only_removed_all_duplicates), in_cfg); + ini_config_destroy(in_cfg); +} +END_TEST + +static Suite *ini_configmod_utils_suite(void) +{ + Suite *s = suite_create("ini_configmod"); + + TCase *tc_delete_properties = tcase_create("delete_properties"); + tcase_add_test(tc_delete_properties, test_delete_value_wrong_arguments); + tcase_add_test(tc_delete_properties, test_delete_value); + + suite_add_tcase(s, tc_delete_properties); + + TCase *tc_update_comments = tcase_create("update_comments"); + tcase_add_test(tc_update_comments, test_update_comments_wrong_arguments); + tcase_add_test(tc_update_comments, test_update_comments); + + suite_add_tcase(s, tc_update_comments); + + TCase *tc_add_string = tcase_create("add_string"); + tcase_add_test(tc_add_string, test_add_str_wrong_arguments); + tcase_add_test(tc_add_string, test_add_str_simple); + tcase_add_test(tc_add_string, test_add_str_duplicate); + tcase_add_test(tc_add_string, test_add_str_update_specific_value); + tcase_add_test(tc_add_string, test_add_str_update_MODADD); + tcase_add_test(tc_add_string, test_add_str_simple_clean); + tcase_add_test(tc_add_string, test_add_str_duplicate_clean); + tcase_add_test(tc_add_string, test_add_str_duplicate_error); + + suite_add_tcase(s, tc_add_string); + + return s; +} + +int main(void) +{ + int number_failed; + + Suite *s = ini_configmod_utils_suite(); + SRunner *sr = srunner_create(s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/sanity/ini_parse_ut_check.c b/tests/sanity/ini_parse_ut_check.c new file mode 100644 index 0000000..830eb84 --- /dev/null +++ b/tests/sanity/ini_parse_ut_check.c @@ -0,0 +1,306 @@ +/* + INI LIBRARY + + Check based unit test for ini parser. + + Copyright (C) Michal Zidek 2016 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#include +#include +#include +#include + +/* #define TRACE_LEVEL 7 */ +#define TRACE_HOME +#include "trace.h" +#include "ini_configobj.h" + +#define TEST_DIR_PATH "" + +START_TEST(test_ini_parse_non_kvp) +{ + int ret; + struct ini_cfgobj *ini_cfg; + int value; + struct ini_cfgfile *file_ctx; + struct value_obj *vo; + char non_kvp_cfg[] = + "[section_before]\n" + "one = 1\n" + + "[section_non_kvp]\n" + "two = 2\n" + "non_kvp\n" + "three = 3\n" + "=nonkvp\n" + + "[section_after]\n" + "four = 4\n"; + + ret = ini_config_file_from_mem(non_kvp_cfg, strlen(non_kvp_cfg), + &file_ctx); + fail_unless(ret == EOK, "Failed to load config. Error %d.\n", ret); + + /* First try without the INI_PARSE_IGNORE_NON_KVP. This should fail + * with error. */ + ret = ini_config_create(&ini_cfg); + fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret); + ret = ini_config_parse(file_ctx, INI_STOP_ON_ERROR, INI_MV1S_ALLOW, 0, + ini_cfg); + fail_if(ret != 5, "Expected error was not found.\n"); + + ini_config_destroy(ini_cfg); + ini_config_file_destroy(file_ctx); + + /* Now try with INI_PARSE_IGNORE_NON_KVP. We should have no errors + * and all the surounding configuration should be valid */ + ret = ini_config_file_from_mem(non_kvp_cfg, strlen(non_kvp_cfg), + &file_ctx); + fail_unless(ret == EOK, "Failed to load config. Error %d.\n", ret); + ret = ini_config_create(&ini_cfg); + fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret); + ret = ini_config_parse(file_ctx, INI_STOP_ON_ERROR, INI_MV1S_ALLOW, + INI_PARSE_IGNORE_NON_KVP, + ini_cfg); + fail_unless(ret == EOK, "ini_config_parse returned %d\n", ret); + + /* Now check if the surrounding configuration is OK */ + /* section_before */ + ret = ini_get_config_valueobj("section_before", "one", ini_cfg, + INI_GET_FIRST_VALUE, &vo); + fail_unless(ret == EOK, "ini_get_config_valueobj returned %d\n: %s", ret, + strerror(ret)); + + value = ini_get_int_config_value(vo, 1, -1, &ret); + fail_unless(ret == EOK, "ini_get_int_config_value returned %d\n: %s", ret, + strerror(ret)); + + fail_unless(ret == EOK); + fail_if(value != 1, "Expected value 1 got %d\n", value); + + /* section_non_kvp */ + ret = ini_get_config_valueobj("section_non_kvp", "two", ini_cfg, + INI_GET_FIRST_VALUE, &vo); + fail_unless(ret == EOK); + + value = ini_get_int_config_value(vo, 1, -1, &ret); + fail_unless(ret == EOK); + fail_if(value != 2, "Expected value 2 got %d\n", value); + + ret = ini_get_config_valueobj("section_non_kvp", "three", ini_cfg, + INI_GET_FIRST_VALUE, &vo); + fail_unless(ret == EOK); + + value = ini_get_int_config_value(vo, 1, -1, &ret); + fail_unless(ret == EOK); + fail_if(value != 3, "Expected value 3 got %d\n", value); + + /* section_after */ + ret = ini_get_config_valueobj("section_after", "four", ini_cfg, + INI_GET_FIRST_VALUE, &vo); + fail_unless(ret == EOK); + + value = ini_get_int_config_value(vo, 1, -1, &ret); + fail_unless(ret == EOK); + fail_if(value != 4, "Expected value 4 got %d\n", value); + + ini_config_destroy(ini_cfg); + ini_config_file_destroy(file_ctx); +} +END_TEST + +START_TEST(test_ini_parse_section_key_conflict) +{ + /* + * This tests the behavior of ini_config_parse to ensure correct handling + * of conflicts between sections and keys of the same name. There are + * three possibilities for conflict: + * + * 1. Inside a section, between the section name and a key name + * 2. Between a default-section key name and a section name + * 3. Between a key name in a different section and a section name + * + * In case (1), parsing finished without an error. However, when + * trying to select a value object inside a section, the returned + * object was an unchecked cast from the section's data, and not the + * attribute's data. In cases (2) and (3), the parser segfaulted while + * trying to merge a section with an attribute. + */ + + char config1[] = + "[a]\n" + "a=a\n"; + + char config2[] = + "a=b\n" + "[a]\n" + "c=d\n"; + + char config3[] = + "[a]\n" + "b=c\n" + "[b]\n" + "a=d\n"; + + char *file_contents[] = {config1, config2, config3, NULL}; + + size_t iter; + + struct ini_cfgobj *ini_config = NULL; + struct ini_cfgfile *file_ctx = NULL; + + int ret; + int i; + int j; + + char **sections = NULL; + int sections_count = 0; + int sections_error = 0; + + char **attributes = NULL; + int attributes_count = 0; + int attributes_error = 0; + + struct value_obj *val = NULL; + char *val_str = NULL; + + for (iter = 0; file_contents[iter] != NULL; iter++) { + ret = ini_config_create(&ini_config); + fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret); + + ret = ini_config_file_from_mem(file_contents[iter], + strlen(file_contents[iter]), + &file_ctx); + fail_unless(ret == EOK, "Failed to load file. Error %d.\n", ret); + + ret = ini_config_parse(file_ctx, 1, 0, 0, ini_config); + fail_unless(ret == EOK, "Failed to parse file. Error %d.\n", ret); + + sections = ini_get_section_list(ini_config, §ions_count, + §ions_error); + fail_unless(sections_error == EOK, + "Failed to get sections. Error %d.\n", + sections_error); + + for (i = 0; i < sections_count; i++) { + attributes = ini_get_attribute_list(ini_config, + sections[i], + &attributes_count, + &attributes_error); + fail_unless(attributes_error == EOK, + "Failed to get attributes. Error %d.\n", + attributes_error); + + for (j = 0; j < attributes_count; j++) { + ret = ini_get_config_valueobj(sections[i], attributes[j], + ini_config, 0, &val); + fail_unless(ret == EOK, + "Failed to get attribute. Error %d.\n", + ret); + + val_str = ini_get_string_config_value(val, &ret); + fail_unless(ret == EOK, + "Failed to get attribute as string. Error %d.\n", + ret); + fail_unless(val_str != NULL, + "Failed to get attribute as string: was NULL.\n"); + + free(val_str); + } + + ini_free_attribute_list(attributes); + } + + ini_free_section_list(sections); + ini_config_file_destroy(file_ctx); + ini_config_destroy(ini_config); + } +} +END_TEST + +/* Maybe we should test even bigger values? */ +#define VALUE_LEN 10000 +/* The +100 is space for section name and key name. */ +#define CFGBUF_LEN (VALUE_LEN + 100) +START_TEST(test_ini_long_value) +{ + int ret; + struct ini_cfgobj *ini_cfg; + struct ini_cfgfile *file_ctx; + struct value_obj *vo; + char big_val_cfg[CFGBUF_LEN] = {0}; + char value[VALUE_LEN] = {0}; + char *value_got; + + /* The value is just a lot of As ending with '\0'*/ + memset(value, 'A', VALUE_LEN - 1); + + /* Create config file */ + ret = snprintf(big_val_cfg, CFGBUF_LEN, "[section]\nkey=%s", value); + + ret = ini_config_file_from_mem(big_val_cfg, strlen(big_val_cfg), + &file_ctx); + fail_unless(ret == EOK, "Failed to load config. Error %d.\n", ret); + + ret = ini_config_create(&ini_cfg); + fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret); + ret = ini_config_parse(file_ctx, INI_STOP_ON_ERROR, INI_MV1S_ALLOW, 0, + ini_cfg); + fail_if(ret != 0, "Failed to parse config. Error %d.\n", ret); + + ret = ini_get_config_valueobj("section", "key", ini_cfg, + INI_GET_FIRST_VALUE, &vo); + fail_unless(ret == EOK, "ini_get_config_valueobj returned %d\n: %s", ret, + strerror(ret)); + + value_got = ini_get_string_config_value(vo, &ret); + fail_unless(ret == EOK, "ini_get_int_config_value returned %d\n: %s", ret, + strerror(ret)); + + fail_unless(strcmp(value, value_got) == 0, "Expected and found values differ!\n"); + free(value_got); + ini_config_destroy(ini_cfg); + ini_config_file_destroy(file_ctx); +} +END_TEST + +static Suite *ini_parse_suite(void) +{ + Suite *s = suite_create("ini_parse_suite"); + + TCase *tc_parse = tcase_create("ini_parse"); + tcase_add_test(tc_parse, test_ini_parse_non_kvp); + tcase_add_test(tc_parse, test_ini_parse_section_key_conflict); + tcase_add_test(tc_parse, test_ini_long_value); + + suite_add_tcase(s, tc_parse); + + return s; +} + +int main(void) +{ + int number_failed; + + Suite *s = ini_parse_suite(); + SRunner *sr = srunner_create(s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/sanity/ini_validators_ut_check.c b/tests/sanity/ini_validators_ut_check.c new file mode 100644 index 0000000..935e16d --- /dev/null +++ b/tests/sanity/ini_validators_ut_check.c @@ -0,0 +1,1108 @@ +/* + INI LIBRARY + + Unit test for the configuration file validators API. + + Copyright (C) Michal Zidek 2016 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* #define TRACE_LEVEL 7 */ +#define TRACE_HOME +#include "trace.h" +#include "ini_configobj.h" + +#define TEST_DIR_PATH "" +#define TEST_RULES_FILE TEST_DIR_PATH"test_rules.ini" + +static void create_rules_from_str(const char *rules, + struct ini_cfgobj **_rules_obj) +{ + FILE *file; + size_t written; + int ret; + + /* We want to test actual reading from file using + * ini_rules_read_from_file, so we create the file here */ + file = fopen(TEST_RULES_FILE, "w"); + fail_if(file == NULL, "fopen() failed: %s", strerror(errno)); + written = fwrite(rules, 1, strlen(rules), file); + fail_unless(written == strlen(rules)); + + /* allow reading */ + ret = chmod(TEST_RULES_FILE, 0664); + fail_unless(ret == 0, "chmod() failed: %s", strerror(errno)); + + fclose(file); + + ret = ini_rules_read_from_file(TEST_RULES_FILE, _rules_obj); + fail_unless(ret == 0, "read_rules_from_file() failed: %s", strerror(ret)); +} + +static struct ini_cfgobj *get_ini_config_from_str(char input_data[], + size_t input_data_len) +{ + struct ini_cfgobj *in_cfg; + struct ini_cfgfile *file_ctx; + int ret; + + ret = ini_config_create(&in_cfg); + fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret); + + ret = ini_config_file_from_mem(input_data, input_data_len, &file_ctx); + fail_unless(ret == EOK, "Failed to load config. Error %d.\n", ret); + + ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, INI_MV1S_ALLOW, 0, + in_cfg); + fail_unless(ret == EOK, "Failed to parse config. Error %d.\n", ret); + + ini_config_file_destroy(file_ctx); + + return in_cfg; +} + +START_TEST(test_ini_errobj) +{ + struct ini_errobj *errobj; + int ret; + const char TEST_MSG1[] = "Test message one."; + const char TEST_MSG2[] = "Test message two."; + const char TEST_MSG3[] = "Test message three."; + + ret = ini_errobj_create(NULL); + fail_unless(ret == EINVAL, + "ini_errobj_create(NULL) failed with wrong error [%s]", + strerror(ret)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + /* We just created the errobj, it should be empty */ + fail_unless(ini_errobj_no_more_msgs(errobj)); + + /* Now add three messages, after adding each message, + * check if the errobj has correct content. */ + ret = ini_errobj_add_msg(errobj, TEST_MSG1); + fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret)); + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG1 was not found."); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ret = ini_errobj_add_msg(errobj, TEST_MSG2); + fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret)); + ini_errobj_reset(errobj); /* strart from first message */ + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG1 was not found."); + ini_errobj_next(errobj); + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG2, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG2 was not found."); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ret = ini_errobj_add_msg(errobj, TEST_MSG3); + fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret)); + ini_errobj_reset(errobj); /* strart from first message */ + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG1 was not found."); + ini_errobj_next(errobj); + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG2, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG2 was not found."); + ini_errobj_next(errobj); + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG3, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG3 was not found."); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); +} +END_TEST + +START_TEST(test_ini_noerror) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + char input_rules[] = + "[rule/always_succeed]\n" + "validator = ini_dummy_noerror\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_error) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + const char *errmsg; + + char input_rules[] = + "[rule/generate_error]\n" + "validator = ini_dummy_error\n"; + + char input_wrong_rule[] = + "[rule/generate_error]\n" + "valid = ini_dummy_error\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate exactly one error */ + fail_if(ini_errobj_no_more_msgs(errobj)); + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, "[rule/generate_error]: Error"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_rules_destroy(rules_obj); + + /* test rule with missing validator */ + create_rules_from_str(input_wrong_rule, &rules_obj); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate exactly one error */ + fail_if(ini_errobj_no_more_msgs(errobj)); + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, "Rule 'rule/generate_error' has no validator."); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_rules_destroy(rules_obj); + ini_config_destroy(cfg_obj); +} +END_TEST + +START_TEST(test_unknown_validator) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + char input_rules[] = + "[rule/always_succeed]\n" + "validator = nonexistent_validator\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate exactly one error */ + fail_if(ini_errobj_no_more_msgs(errobj)); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +static int custom_noerror(const char *rule_name, + struct ini_cfgobj *rules_obj, + struct ini_cfgobj *config_obj, + struct ini_errobj *errobj, + void **data) +{ + return 0; +} + +static int custom_error(const char *rule_name, + struct ini_cfgobj *rules_obj, + struct ini_cfgobj *config_obj, + struct ini_errobj *errobj, + void **data) +{ + return ini_errobj_add_msg(errobj, "Error"); +} + +START_TEST(test_custom_noerror) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + struct ini_validator *noerror[] = { + &(struct ini_validator){ "custom_noerror", custom_noerror, NULL }, + NULL + }; + struct ini_validator *missing_name[] = { + &(struct ini_validator){ NULL, custom_noerror, NULL }, + &(struct ini_validator){ "custom_noerror", custom_noerror, NULL }, + NULL + }; + + char input_rules[] = + "[rule/custom_succeed]\n" + "validator = custom_noerror\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + /* Pass the custom validator to ini_rules_check() */ + ret = ini_rules_check(rules_obj, cfg_obj, noerror, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate no errors */ + fail_unless(ini_errobj_no_more_msgs(errobj)); + + /* Pass wrong external validator to ini_rules_check() */ + /* It should be skipped */ + ret = ini_rules_check(rules_obj, cfg_obj, missing_name, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate no errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), "%s", ini_errobj_get_msg(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_custom_error) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + struct ini_validator *error[] = { + &(struct ini_validator){ "custom_error", custom_error, NULL }, + NULL + }; + struct ini_validator *missing_function[] = { + &(struct ini_validator){ "custom_noerror", NULL, NULL }, + NULL + }; + const char *errmsg; + + char input_rules[] = + "[rule/custom_error]\n" + "validator = custom_error\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + /* Pass the custom validator to ini_rules_check() */ + ret = ini_rules_check(rules_obj, cfg_obj, error, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate one error */ + fail_if(ini_errobj_no_more_msgs(errobj)); + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, "[rule/custom_error]: Error"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + /* Pass the custom validator to ini_rules_check() */ + ret = ini_rules_check(rules_obj, cfg_obj, missing_function, errobj); + + /* Should generate one error for missing validator */ + fail_if(ini_errobj_no_more_msgs(errobj)); + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "Rule 'rule/custom_error' uses unknown validator " + "'custom_error'."); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + + ini_rules_destroy(rules_obj); + ini_config_destroy(cfg_obj); +} +END_TEST + +START_TEST(test_ini_allowed_options_ok) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/options_for_foo]\n" + "validator = ini_allowed_options\n" + "section_re = ^foo$\n" + "option = bar\n" + "option = baz\n"; + + /* Should check only foo section, other sections are + * irrelevant and can contain any option */ + char input_cfg[] = + "[foo]\n" + "bar = 0\n" + "baz = 0\n" + "[oof]\n" + "opt1 = 1\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate no errors */ + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_options_no_section) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + size_t num_err; + const char *errmsg; + + /* Ommit section_re to generate error */ + char input_rules[] = + "[rule/options_for_foo]\n" + "validator = ini_allowed_options\n" + /* "section_re = ^foo$\n" */ + "option = bar\n" + "option = baz\n"; + + /* section_re without value */ + char input_rules2[] = + "[rule/options_for_foo]\n" + "validator = ini_allowed_options\n" + "section_re = \n" + "option = bar\n" + "option = baz\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[foo]\n" + "bar = 0\n" + "baz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors (one from rules_check and one + * from the validator itself) */ + fail_if(ini_errobj_no_more_msgs(errobj)); + + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "Rule 'rule/options_for_foo' returned error code '22'"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "[rule/options_for_foo]: Validator misses 'section_re' " + "parameter"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_rules_destroy(rules_obj); + + /* the second test with missing value for section_re */ + + create_rules_from_str(input_rules2, &rules_obj); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors (one from rules_check and one + * from the validator itself) */ + fail_if(ini_errobj_no_more_msgs(errobj)); + + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "Rule 'rule/options_for_foo' returned error code '22'"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "[rule/options_for_foo]: Validator misses 'section_re' " + "parameter"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_rules_destroy(rules_obj); + + ini_config_destroy(cfg_obj); +} +END_TEST + +START_TEST(test_ini_allowed_options_wrong_regex) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + size_t num_err; + const char *errmsg; + + /* Ommit section_re to generate error */ + char input_rules[] = + "[rule/options_for_foo]\n" + "validator = ini_allowed_options\n" + "section_re = ^foo[$\n" + "option = bar\n" + "option = baz\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[foo]\n" + "bar = 0\n" + "baz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors (one from rules_check and one + * from the validator itself) */ + fail_if(ini_errobj_no_more_msgs(errobj)); + + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "Rule 'rule/options_for_foo' returned error code '22'"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + + /* Different versions of libc produce slightly different error strings + * in this case. For simplicity compare against all of them. */ + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "[rule/options_for_foo]: Cannot compile regular expression " + "from option 'section_re'. " + "Error: 'Unmatched [ or [^'"); + if (ret != 0) { + ret = strcmp(errmsg, + "[rule/options_for_foo]: Cannot compile regular " + "expression from option 'section_re'. " + "Error: 'brackets ([ ]) not balanced'"); + } + + if (ret != 0) { + ret = strcmp(errmsg, + "[rule/options_for_foo]: Cannot compile regular " + "expression from option 'section_re'. " + "Error: 'Unmatched [, [^, [:, [., or [='"); + } + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_options_typos) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + size_t num_err; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/options_for_foo]\n" + "validator = ini_allowed_options\n" + "section_re = ^foo$\n" + "option = bar\n" + "option = baz\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[foo]\n" + "br = 0\n" + "bra = 0\n" + "abr = 0\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 4 errors */ + fail_if(ini_errobj_no_more_msgs(errobj)); + + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 4, "Expected 4 errors, got %d", num_err); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_str_ok) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section = foo\n" + "section = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[foo]\n" + "br = 0\n" + "bra = 0\n" + "[bar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 0 errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), + "Unexpected errors found: [%s]", ini_errobj_get_msg(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_str_typos) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int num_err; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section = foo\n" + "section = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[fooo]\n" + "br = 0\n" + "bra = 0\n" + "[baar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors */ + fail_if(ini_errobj_no_more_msgs(errobj), + "Expected 2 errors but none found"); + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_str_insensitive) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + int i; + + /* Only bar and baz are allowed for foo section */ + char input_rules_template[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "case_insensitive = %s\n" + "section = foo\n" + "section = bar\n"; + + char input_rules[sizeof(input_rules_template) + 10]; + + const char *case_insensitive_values[] = { "yes", "Yes", "true", "True", + "1", NULL }; + /* Make 4 typos */ + char input_cfg[] = + "[FOo]\n" + "br = 0\n" + "bra = 0\n" + "[baR]\n" + "abz = 0\n"; + + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + for (i = 0; case_insensitive_values[i] != NULL; i++) { + sprintf(input_rules, input_rules_template, case_insensitive_values[i]); + + create_rules_from_str(input_rules, &rules_obj); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, + "ini_errobj_create() failed for case_insensitive = %s: %s", + case_insensitive_values[i], strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, + "ini_rules_check() failed for case_insensitive = %s: %s", + case_insensitive_values[i], strerror(ret)); + + /* Should generate 0 errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), + "Unexpected errors found for case_insensitive = %s: [%s]", + case_insensitive_values[i], ini_errobj_get_msg(errobj)); + + ini_errobj_destroy(&errobj); + ini_rules_destroy(rules_obj); + } + + ini_config_destroy(cfg_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_re_ok) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section_re = ^foo*$\n" + "section_re = bar\n"; + + char input_cfg[] = + "[foooooooooooo]\n" + "br = 0\n" + "bra = 0\n" + "[my_bar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 0 errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found"); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_re_typos) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int num_err; + int ret; + + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section_re = ^foo*$\n" + "section_re = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[fooooooOooooo]\n" + "br = 0\n" + "bra = 0\n" + "[my_bra]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors */ + fail_if(ini_errobj_no_more_msgs(errobj), + "Expected 2 errors but none found"); + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_re_insensitive) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "case_insensitive = yes\n" + "section_re = ^foo*$\n" + "section_re = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[FOoOoOoOoOOOOooo]\n" + "br = 0\n" + "bra = 0\n" + "[my_Bar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 0 errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found"); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_missing_section) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int num_err; + int ret; + const char *errmsg; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[fooo]\n" + "br = 0\n" + "bra = 0\n" + "[baar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 1 errors */ + fail_if(ini_errobj_no_more_msgs(errobj), + "Expected 1 errors but none found"); + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 1, "Expected 1 errors, got %d", num_err); + + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "[rule/section_list]: No allowed sections specified. " + "Use 'section = default' to allow only default section"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_wrong_regex) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int num_err; + int ret; + const char *errmsg; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section_re = ^foo\\(*$\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[fooo]\n" + "br = 0\n" + "bra = 0\n" + "[baar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors */ + fail_if(ini_errobj_no_more_msgs(errobj), + "Expected 2 errors but none found"); + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "Rule 'rule/section_list' returned error code '22'"); + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, + "[rule/section_list]: Validator failed to use regex " + "[^foo\\(*$]:[Unmatched ( or \\(]"); + if (ret !=0) { + ret = strcmp(errmsg, + "[rule/section_list]: Validator failed to use regex " + "[^foo\\(*$]:[parentheses not balanced]"); + } + fail_unless(ret == 0, "Got msg: [%s]", errmsg); + ini_errobj_next(errobj); + + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +static Suite *ini_validators_utils_suite(void) +{ + Suite *s = suite_create("ini_validators"); + + TCase *tc_infrastructure = tcase_create("infrastructure"); + tcase_add_test(tc_infrastructure, test_ini_errobj); + tcase_add_test(tc_infrastructure, test_ini_noerror); + tcase_add_test(tc_infrastructure, test_ini_error); + tcase_add_test(tc_infrastructure, test_unknown_validator); + tcase_add_test(tc_infrastructure, test_custom_noerror); + tcase_add_test(tc_infrastructure, test_custom_error); + + TCase *tc_allowed_options = tcase_create("ini_allowed_options"); + tcase_add_test(tc_allowed_options, test_ini_allowed_options_ok); + tcase_add_test(tc_allowed_options, test_ini_allowed_options_no_section); + tcase_add_test(tc_allowed_options, test_ini_allowed_options_wrong_regex); + tcase_add_test(tc_allowed_options, test_ini_allowed_options_typos); + + TCase *tc_allowed_sections = tcase_create("ini_allowed_sections"); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_str_ok); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_str_typos); + tcase_add_test(tc_allowed_sections, + test_ini_allowed_sections_str_insensitive); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_re_ok); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_re_typos); + tcase_add_test(tc_allowed_sections, + test_ini_allowed_sections_re_insensitive); + tcase_add_test(tc_allowed_sections, + test_ini_allowed_sections_missing_section); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_wrong_regex); + + suite_add_tcase(s, tc_infrastructure); + suite_add_tcase(s, tc_allowed_options); + suite_add_tcase(s, tc_allowed_sections); + + return s; +} + +int main(void) +{ + int number_failed; + + Suite *s = ini_validators_utils_suite(); + SRunner *sr = srunner_create(s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/sanity/ini_valueobj_ut.c b/tests/sanity/ini_valueobj_ut.c new file mode 100644 index 0000000..558ac11 --- /dev/null +++ b/tests/sanity/ini_valueobj_ut.c @@ -0,0 +1,741 @@ +/* + INI LIBRARY + + Unit test for the value object. + + Copyright (C) Dmitri Pal 2010 + + 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; either version 3 of the License, or + (at your option) any later version. + 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, see . +*/ + +#include /* for errors */ +#include +#include +#include +#include +#include + +#include "ini_valueobj.h" +#define TRACE_HOME +#include "trace.h" + +#define TEST_SIZE 80 + +int verbose = 0; + +#define VOOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + + +typedef int (*test_fn)(void); + +static int create_comment(int i, struct ini_comment **ic) +{ + int error = EOK; + const char *template = ";Line 0 of the value %d"; + char comment[TEST_SIZE]; + struct ini_comment *new_ic = NULL; + + TRACE_FLOW_ENTRY(); + + snprintf(comment, TEST_SIZE, template, i); + + + if ((error = ini_comment_create(&new_ic)) || + (error = ini_comment_build(new_ic, comment)) || + (error = ini_comment_build(new_ic, NULL)) || + (error = ini_comment_build(new_ic, "#This is the second line")) || + (error = ini_comment_build(new_ic, ";This is the third line")) || + (error = ini_comment_build(new_ic, ""))) { + printf("Failed to create comment object. Error %d.\n", error); + ini_comment_destroy(new_ic); + return -1; + } + + *ic = new_ic; + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Save value to the file */ +/* NOTE: might be moved into the API in future */ +static int save_value(FILE *ff, const char *key, struct value_obj *vo) +{ + + int error = EOK; + struct simplebuffer *sbobj = NULL; + uint32_t left = 0; + + TRACE_FLOW_ENTRY(); + + error = simplebuffer_alloc(&sbobj); + if (error) { + TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error); + return error; + } + + /* Serialize */ + error = value_serialize(vo, key, sbobj); + if (error) { + printf("Failed to serialize a value object %d.\n", error); + simplebuffer_free(sbobj); + return error; + } + + /* Add CR */ + error = simplebuffer_add_cr(sbobj); + if (error) { + TRACE_ERROR_NUMBER("Failed to add CR", error); + simplebuffer_free(sbobj); + return error; + } + + /* Save */ + left = simplebuffer_get_len(sbobj); + while (left > 0) { + error = simplebuffer_write(fileno(ff), sbobj, &left); + if (error) { + printf("Failed to write value object %d.\n", error); + simplebuffer_free(sbobj); + return error; + } + } + + simplebuffer_free(sbobj); + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Test to create value object using arrays */ +static int other_create_test(FILE *ff, struct value_obj **vo) +{ + int error = EOK; + struct value_obj *new_vo = NULL; + struct ref_array *raw_lines; + struct ref_array *raw_lengths; + struct ini_comment *ic = NULL; + struct ini_comment *ic2 = NULL; + char *val; + const char *vallines[] = { "Domain1,", + " Domain2 ,", + " Domain3 " }; + const char *fullstr; + const char *expected = "Domain1, Domain2 , Domain3"; + int i; + uint32_t origin = 0; + uint32_t line = 0; + uint32_t len = 0; + uint32_t expected_len = 0; + + + TRACE_FLOW_ENTRY(); + + /* Create a pair of arrays */ + error = value_create_arrays(&raw_lines, + &raw_lengths); + if (error) { + printf("Failed to create arrays %d.\n", error); + return error; + } + + for (i=0; i< 3; i++) { + errno = 0; + val = strdup(vallines[i]); + if (val == NULL) { + error = errno; + printf("Failed to dup memory %d.\n", error); + value_destroy_arrays(raw_lines, + raw_lengths); + return error; + } + + /* Add line to the arrays */ + error = value_add_to_arrays(val, + strlen(val), + raw_lines, + raw_lengths); + if (error) { + printf("Failed to add to arrays %d.\n", error); + value_destroy_arrays(raw_lines, + raw_lengths); + return error; + } + + } + + /* Create a comment */ + error = create_comment(1000, &ic); + if (error) { + printf("Failed to create comment %d.\n", error); + value_destroy_arrays(raw_lines, + raw_lengths); + return error; + } + + /* Create object */ + error = value_create_from_refarray(raw_lines, + raw_lengths, + 1, + INI_VALUE_READ, + 3, + 70, + ic, + &new_vo); + + if (error) { + printf("Failed to create comment %d.\n", error); + value_destroy_arrays(raw_lines, + raw_lengths); + ini_comment_destroy(ic); + return error; + } + + /* Save value to the file */ + error = save_value(ff, "baz", new_vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + value_destroy(new_vo); + return error; + } + + /* Now do assertions and modifications to the object */ + + /* NOTE: Below this line do not need to free arrays or comment + * they became internal parts of the value object + * and will be freed as a part of it. + */ + + /* Get concatenated value */ + error = value_get_concatenated(new_vo, + &fullstr); + + if (error) { + printf("Failed to get the string %d.\n", error); + value_destroy(new_vo); + return error; + } + + /* Get length of the concatenated value */ + value_get_concatenated_len(new_vo, &len); + expected_len = strlen(expected); + + if ((len != expected_len) || + (strncmp(fullstr, expected, expected_len + 1) != 0)) { + printf("The expected value is different.\n"); + printf("The expected value is %s\n", expected); + printf("The real value is %s\n", fullstr); + printf("The expected len is %d, real %d.\n", expected_len, len); + value_destroy(new_vo); + return EINVAL; + } + + /* Get value's origin */ + error = value_get_origin(new_vo, &origin); + if (error) { + printf("Failed to get origin %d.\n", error); + value_destroy(new_vo); + return error; + } + + if (origin != INI_VALUE_READ) { + printf("The expected origin is different.\n%d\n", origin); + value_destroy(new_vo); + return EINVAL; + } + + /* Get value's line */ + error = value_get_line(new_vo, &line); + if (error) { + printf("Failed to get origin %d.\n", error); + value_destroy(new_vo); + return error; + } + + if (line != 1) { + printf("The expected line is different.\n%d\n", origin); + value_destroy(new_vo); + return EINVAL; + } + + /* Get comment from the value */ + ic = NULL; + error = value_extract_comment(new_vo, &ic); + if (error) { + printf("Failed to extract comment %d.\n", error); + value_destroy(new_vo); + return error; + } + + if (ic == NULL) { + printf("The expected comment to be there.\n"); + value_destroy(new_vo); + return error; + } + + VOOUT(ini_comment_print(ic, stdout)); + + /* Get comment again */ + ic2 = NULL; + error = value_extract_comment(new_vo, &ic2); + if (error) { + printf("Failed to extract comment %d.\n", error); + value_destroy(new_vo); + ini_comment_destroy(ic); + return error; + } + + if (ic2 != NULL) { + printf("The expected NO comment to be there.\n"); + value_destroy(new_vo); + ini_comment_destroy(ic); + /* No free for ic2 since it is the same object */ + + /* But this should not happen anyways - + * it will be coding error. + */ + return EINVAL; + } + + /* Put comment back */ + error = value_put_comment(new_vo, ic); + if (error) { + printf("Failed to put comment back %d.\n", error); + value_destroy(new_vo); + ini_comment_destroy(ic); + return error; + } + + /* Save value to the file */ + error = save_value(ff, "bar", new_vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + value_destroy(new_vo); + return error; + } + + *vo = new_vo; + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Modify the value object */ +static int modify_test(FILE *ff, struct value_obj *vo) +{ + int error = EOK; + const char *strval = "Domain100, Domain200, Domain300"; + + TRACE_FLOW_ENTRY(); + + + /* Update key length */ + error = value_set_keylen(vo, strlen("foobar")); + if (error) { + printf("Failed to change key length %d.\n", error); + return error; + } + + /* Update value */ + error = value_update(vo, + strval, + strlen(strval), + INI_VALUE_CREATED, + 10); + if (error) { + printf("Failed to update value %d.\n", error); + return error; + } + + + /* Save value to the file */ + error = save_value(ff, "foobar", vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + return error; + } + + TRACE_FLOW_EXIT(); + return EOK; +} + + +static int vo_basic_test(void) +{ + int error = EOK; + const char *strvalue = "Test multi_word_value_that_will_" + "be_split_between_several_lines_!"; + + /* Other testing can be done with the following string: + * const char *strvalue = "Test multi word value that " + * "will be split between several lines"; + */ + + struct value_obj *vo = NULL; + uint32_t wrap = 0; + struct ini_comment *ic = NULL; + FILE *ff = NULL; + + TRACE_FLOW_ENTRY(); + + errno = 0; + ff = fopen("test.ini","wt"); + if (ff == NULL) { + error = errno; + printf("Failed to open file. Error %d.\n", error); + return error; + } + + + for (wrap = 0; wrap < 80; wrap++) { + + ic = NULL; + error = create_comment(wrap, &ic); + if (error) { + printf("Failed to create a new comment object %d.\n", error); + fclose(ff); + return error; + } + + vo = NULL; + error = value_create_new(strvalue, + strlen(strvalue), + INI_VALUE_CREATED, + 3, + wrap, + ic, + &vo); + if (error) { + printf("Failed to create a new value object %d.\n", error); + ini_comment_destroy(ic); + fclose(ff); + return error; + } + + error = save_value(ff, "key", vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + value_destroy(vo); + fclose(ff); + return error; + } + + value_destroy(vo); + } + + vo = NULL; + + /* Run other create test here */ + error = other_create_test(ff, &vo); + if (error) { + printf("Create test failed %d.\n", error); + fclose(ff); + return error; + } + + /* Run modify test here */ + error = modify_test(ff, vo); + if (error) { + printf("Modify test failed %d.\n", error); + fclose(ff); + value_destroy(vo); + return error; + } + + value_destroy(vo); + + + ic = NULL; + error = create_comment(100, &ic); + if (error) { + printf("Failed to create a new comment object %d.\n", error); + fclose(ff); + return error; + } + + ini_comment_print(ic, ff); + + ini_comment_destroy(ic); + + fclose(ff); + + TRACE_FLOW_EXIT(); + return EOK; +} + +static int vo_copy_test(void) +{ + int error = EOK; + const char *strvalue = "Test multi word value that " + "will be split between several lines"; + + struct value_obj *vo = NULL; + struct value_obj *vo_copy = NULL; + uint32_t wrap = 0; + struct ini_comment *ic = NULL; + FILE *ff = NULL; + char comment[100]; + + TRACE_FLOW_ENTRY(); + + VOOUT(printf("Copy test\n")); + + errno = 0; + ff = fopen("test.ini","a"); + if (ff == NULL) { + error = errno; + printf("Failed to open file. Error %d.\n", error); + return error; + } + + error = ini_comment_create(&ic); + if (error) { + printf("Failed to create comment object\n"); + fclose(ff); + return -1; + } + + error = ini_comment_append(ic, "#This is a copy test!"); + if (error) { + printf("Failed to add a line to the comment %d.\n", error); + ini_comment_destroy(ic); + fclose(ff); + return error; + } + + error = ini_comment_append(ic, "#Replacable comment line"); + if (error) { + printf("Failed to add a line to the comment %d.\n", error); + ini_comment_destroy(ic); + fclose(ff); + return error; + } + + error = value_create_new(strvalue, + strlen(strvalue), + INI_VALUE_CREATED, + 3, + 20, + ic, + &vo); + if (error) { + printf("Failed to create a new value object %d.\n", error); + ini_comment_destroy(ic); + fclose(ff); + return error; + } + + error = save_value(ff, "key", vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + value_destroy(vo); + fclose(ff); + return error; + } + + for (wrap = 0; wrap < 80; wrap++) { + + TRACE_INFO_NUMBER("Iteration:", wrap); + + vo_copy = NULL; + error = value_copy(vo, &vo_copy); + if (error) { + printf("Failed to create a new value object %d.\n", error); + value_destroy(vo); + fclose(ff); + return error; + } + + error = value_set_boundary(vo_copy, wrap); + if (error) { + printf("Failed to set boundary %d.\n", error); + value_destroy(vo); + value_destroy(vo_copy); + fclose(ff); + return error; + } + + /* Get comment from the value */ + ic = NULL; + error = value_extract_comment(vo_copy, &ic); + if (error) { + printf("Failed to extract comment %d.\n", error); + value_destroy(vo); + value_destroy(vo_copy); + fclose(ff); + return error; + } + + /* Replace comment in the value */ + snprintf(comment, TEST_SIZE, ";This is value with boundary %d", wrap); + VOOUT(printf("Comment: %s\n", comment)); + error = ini_comment_replace(ic, 1, comment); + if (error) { + printf("Failed to replace comment %d.\n", error); + value_destroy(vo); + value_destroy(vo_copy); + fclose(ff); + return error; + } + + /* Set comment into the value */ + error = value_put_comment(vo_copy, ic); + if (error) { + printf("Failed to set comment %d.\n", error); + value_destroy(vo); + value_destroy(vo_copy); + fclose(ff); + return error; + } + + error = save_value(ff, "key", vo_copy); + if (error) { + printf("Failed to save value to file %d.\n", error); + value_destroy(vo); + value_destroy(vo_copy); + fclose(ff); + return error; + } + + value_destroy(vo_copy); + } + + value_destroy(vo); + fclose(ff); + TRACE_FLOW_EXIT(); + return EOK; +} + +static int vo_show_test(void) +{ + VOOUT(system("cat test.ini")); + return EOK; +} + +static int vo_mc_test(void) +{ + int error = EOK; + struct value_obj *vo1 = NULL; + struct value_obj *vo2 = NULL; + struct ini_comment *ic = NULL; + + TRACE_FLOW_ENTRY(); + + VOOUT(printf("<=== Merge Comment Test ===>\n")); + + error = create_comment(1, &ic); + if (error) { + printf("Failed to create a new comment object %d.\n", error); + return error; + } + + error = value_create_new("test1", + 5, + INI_VALUE_CREATED, + 3, + 80, + ic, + &vo1); + if (error) { + printf("Failed to create the first value object %d.\n", error); + ini_comment_destroy(ic); + return error; + } + + error = create_comment(2, &ic); + if (error) { + printf("Failed to create a new comment object %d.\n", error); + value_destroy(vo1); + return error; + } + + error = value_create_new("test2", + 5, + INI_VALUE_CREATED, + 3, + 80, + ic, + &vo2); + if (error) { + printf("Failed to create the second value object %d.\n", error); + ini_comment_destroy(ic); + value_destroy(vo1); + return error; + } + + /* Merge comment from one value into another */ + error = value_merge_comment(vo2, vo1); + if (error) { + printf("Failed to merge comments %d.\n", error); + value_destroy(vo1); + value_destroy(vo2); + return error; + } + + value_destroy(vo2); + + VOOUT(printf("<=== Key ===>\n")); + VOOUT(value_print("key", vo1)); + + error = value_extract_comment(vo1, &ic); + if (error) { + printf("Failed to extract comment %d.\n", error); + value_destroy(vo1); + return error; + } + + value_destroy(vo1); + + VOOUT(printf("<=== Comment ===>\n")); + VOOUT(ini_comment_print(ic, stdout)); + ini_comment_destroy(ic); + + TRACE_FLOW_EXIT(); + return EOK; +} + + +/* Main function of the unit test */ +int main(int argc, char *argv[]) +{ + int error = 0; + test_fn tests[] = { vo_basic_test, + vo_copy_test, + vo_show_test, + vo_mc_test, + NULL }; + test_fn t; + int i = 0; + char *var; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + else { + var = getenv("COMMON_TEST_VERBOSE"); + if (var) verbose = 1; + } + + VOOUT(printf("Start\n")); + + while ((t = tests[i++])) { + error = t(); + if (error) { + VOOUT(printf("Failed with error %d!\n", error)); + return error; + } + } + + VOOUT(printf("Success!\n")); + return 0; +} diff --git a/tests/sanity/path_utils_ut.c b/tests/sanity/path_utils_ut.c new file mode 100644 index 0000000..04d7018 --- /dev/null +++ b/tests/sanity/path_utils_ut.c @@ -0,0 +1,768 @@ +/* + path_utils - unit tests + + Authors: + Jakub Hrozek + + Copyright (C) 2009 Red Hat + + 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; either version 3 of the License, or + (at your option) any later version. + + 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, see . +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "path_utils.h" + +#define fail_unless_str_equal(a, b) do { \ + fail_unless(strcmp(a, b) == 0, \ + "The strings '%s' and '%s' are different, expected same", \ + a, b); \ +} while(0); + +#define DIR_TEMPLATE "test-directory-list-dir-XXXXXX" +#define SUBDIR "test-directory-list-subdir" +#define SUBSUBDIR "test-directory-list-subsubdir" +char *dlist_dir; +char *dlist_subdir; +char *dlist_subsubdir; + +/**** get_dirname ****/ +START_TEST(test_dirname) +{ + char p[PATH_MAX]; + char cwd[PATH_MAX]; + + fail_unless(get_dirname(p, PATH_MAX, "/foo/bar") == SUCCESS); + fail_unless_str_equal(p, "/foo"); + + fail_unless(get_dirname(p, PATH_MAX, "/") == SUCCESS); + fail_unless_str_equal(p, "/"); + + fail_unless(get_dirname(p, PATH_MAX, "/foo") == SUCCESS); + fail_unless_str_equal(p, "/"); + + fail_unless(get_dirname(p, PATH_MAX, "//foo//bar") == SUCCESS); + fail_unless_str_equal(p, "//foo"); + + fail_unless(get_dirname(p, PATH_MAX, "//foo//") == SUCCESS); + fail_unless(!strcmp(p, "/") || !strcmp(p, "//")); + + fail_unless(get_dirname(p, PATH_MAX, "foo//bar") == SUCCESS); + fail_unless_str_equal(p, "foo"); + + fail_unless(get_dirname(p, PATH_MAX, "foo//////bar") == SUCCESS); + fail_unless_str_equal(p, "foo"); + + /* if pathname does not contain a slash, dirname returns cwd */ + fail_if(getcwd(cwd, PATH_MAX) == NULL, "getcwd failed"); + + fail_unless(get_dirname(p, PATH_MAX, "foo") == SUCCESS); + fail_unless_str_equal(p, cwd); + + fail_unless(get_dirname(p, PATH_MAX, ".") == SUCCESS); + fail_unless_str_equal(p, cwd); + + fail_unless(get_dirname(p, PATH_MAX, "..") == SUCCESS); + fail_unless_str_equal(p, cwd); + + fail_unless(get_dirname(p, PATH_MAX, "") == SUCCESS); + fail_unless_str_equal(p, cwd); +} +END_TEST + +START_TEST(test_dirname_neg) +{ + char neg[3]; + char p[PATH_MAX]; + + fail_if(get_dirname(neg, 3, "/foo/bar") == SUCCESS); + fail_unless(get_dirname(p, PATH_MAX, NULL) == EINVAL); +} +END_TEST + +/**** get_basename ****/ +START_TEST(test_basename) +{ + char p[PATH_MAX]; + char cwd[PATH_MAX]; + + fail_unless(get_basename(p, PATH_MAX, "/foo/bar") == SUCCESS); + fail_unless_str_equal(p, "bar"); + + fail_unless(get_basename(p, PATH_MAX, "/foo/") == SUCCESS); + fail_unless_str_equal(p, "foo"); + + fail_unless(get_basename(p, PATH_MAX, "foo") == SUCCESS); + fail_unless_str_equal(p, "foo"); + + fail_unless(get_basename(p, PATH_MAX, "/") == SUCCESS); + fail_unless_str_equal(p, "/"); + + fail_if(getcwd(cwd, PATH_MAX) == NULL, "getcwd failed"); + + fail_unless(get_basename(p, PATH_MAX, ".") == SUCCESS); + fail_unless_str_equal(p, cwd); + + fail_unless(get_basename(p, PATH_MAX, "") == SUCCESS); + fail_unless_str_equal(p, cwd); +} +END_TEST + +START_TEST(test_basename_neg) +{ + char neg[3]; + char p[PATH_MAX]; + + fail_if(get_basename(neg, 3, "/foo/bar") == SUCCESS); + + fail_unless(get_basename(p, PATH_MAX, NULL) == EINVAL); +} +END_TEST + +/**** is_absolute_path ****/ +START_TEST(test_is_absolute_path) +{ + fail_unless(is_absolute_path("") == false); + fail_unless(is_absolute_path("foo/bar") == false); + + fail_unless(is_absolute_path("/foo/bar") == true); + fail_unless(is_absolute_path("/foo") == true); + fail_unless(is_absolute_path("/") == true); +} +END_TEST + +/**** get_dirname_and_basename ****/ +/* Just a couple of basic tests - get_dirname_and_basename() + * uses get_dirname() and get_basename() under the hood which + * are tested enough in their specific tests + */ +START_TEST(test_dirname_and_basename) +{ + char dir[PATH_MAX]; + char base[PATH_MAX]; + char cwd[PATH_MAX]; + int ret; + + ret = get_directory_and_base_name(dir, PATH_MAX, base, PATH_MAX, "/foo/bar"); + fail_unless(ret == SUCCESS); + fail_unless_str_equal(dir, "/foo"); + fail_unless_str_equal(base, "bar"); + + ret = get_directory_and_base_name(dir, PATH_MAX, base, PATH_MAX, "/foo"); + fail_unless(ret == SUCCESS); + fail_unless_str_equal(dir, "/"); + fail_unless_str_equal(base, "foo"); + + ret = get_directory_and_base_name(dir, PATH_MAX, base, PATH_MAX, "/"); + fail_unless(ret == SUCCESS); + fail_unless_str_equal(dir, "/"); + fail_unless_str_equal(base, "/"); + + fail_if(getcwd(cwd, PATH_MAX) == NULL, "getcwd failed"); + + ret = get_directory_and_base_name(dir, PATH_MAX, base, PATH_MAX, "foo"); + fail_unless(ret == SUCCESS); + fail_unless_str_equal(dir, cwd); + fail_unless_str_equal(base, "foo"); + + ret = get_directory_and_base_name(dir, PATH_MAX, base, PATH_MAX, ""); + fail_unless(ret == SUCCESS); + fail_unless_str_equal(dir, cwd); + fail_unless_str_equal(base, ""); + + ret = get_directory_and_base_name(dir, PATH_MAX, base, PATH_MAX, "."); + fail_unless(ret == SUCCESS); + fail_unless_str_equal(dir, cwd); + fail_unless_str_equal(base, ""); +} +END_TEST + +START_TEST(test_dirname_and_basename_neg) +{ + char dir[PATH_MAX]; + char base[PATH_MAX]; + int ret; + + ret = get_directory_and_base_name(dir, PATH_MAX, base, PATH_MAX, NULL); + fail_unless(ret == EINVAL); +} +END_TEST + +/**** path_concat ****/ +START_TEST(test_path_concat) +{ + char p[PATH_MAX]; + char p2[9]; + + fail_unless(path_concat(p, PATH_MAX, "/foo", "bar") == SUCCESS); + fail_unless_str_equal(p, "/foo/bar"); + + fail_unless(path_concat(p, PATH_MAX, "/foo", "/bar") == SUCCESS); + fail_unless_str_equal(p, "/foo/bar"); + + fail_unless(path_concat(p, PATH_MAX, "/foo/", "/bar") == SUCCESS); + fail_unless_str_equal(p, "/foo/bar"); + + fail_unless(path_concat(p, PATH_MAX, "/foo", "") == SUCCESS); + fail_unless_str_equal(p, "/foo"); + + fail_unless(path_concat(p, PATH_MAX, "foo", NULL) == SUCCESS); + fail_unless_str_equal(p, "foo"); + + fail_unless(path_concat(p, PATH_MAX, "", "foo") == SUCCESS); + fail_unless_str_equal(p, "foo"); + + fail_unless(path_concat(p, PATH_MAX, "/", "foo") == SUCCESS); + fail_unless_str_equal(p, "/foo"); + + fail_unless(path_concat(p, PATH_MAX, "/foo", "/") == SUCCESS); + fail_unless_str_equal(p, "/foo/"); + + fail_unless(path_concat(p, PATH_MAX, "/foo", "bar/") == SUCCESS); + fail_unless_str_equal(p, "/foo/bar/"); + + fail_unless(path_concat(p, PATH_MAX, NULL, "foo") == SUCCESS); + fail_unless_str_equal(p, "foo"); + + /* on-by-one */ + fail_unless(path_concat(p2, 9, "/foo", "bar") == SUCCESS); + fail_unless_str_equal(p2, "/foo/bar"); +} +END_TEST + +START_TEST(test_path_concat_neg) +{ + char small[3]; + char small2[5]; + char small3[7]; + char p2[10]; + + /* these two test different conditions */ + + /* Test if head is longer than the buffer */ + fail_unless(path_concat(small, 3, "/foo", "bar") == ENOBUFS); + /* On ENOBUFS, path should be empty */ + fail_unless_str_equal(small, ""); + + /* Test if head is the same length as the buffer */ + fail_unless(path_concat(small2, 5, "/foo", "bar") == ENOBUFS); + /* On ENOBUFS, path should be empty */ + fail_unless_str_equal(small2, ""); + + /* Test if head+tail is the longer than the buffer */ + fail_unless(path_concat(small3, 7, "/foo", "bar") == ENOBUFS); + /* On ENOBUFS, path should be empty */ + fail_unless_str_equal(small3, ""); + + /* off-by-one */ + /* Fill with garbage data for now */ + memset(p2, 'Z', 9); + p2[9] = '\0'; + + fail_unless(path_concat(p2, 8, "/foo", "bar") == ENOBUFS); + /* Make sure we don't write past the end of the buffer */ + fail_unless(p2[8] == 'Z', "Got [%d]", p2[8]); + /* On ENOBUFS, path should be empty */ + fail_unless_str_equal(p2, ""); +} +END_TEST + +/**** make_path_absolute ****/ +START_TEST(test_make_path_absolute) +{ + char p[PATH_MAX]; + char p2[PATH_MAX]; + char cwd[PATH_MAX]; + char *buf; + size_t buf_len; + + fail_unless(make_path_absolute(p, PATH_MAX, "/foo") == SUCCESS); + fail_unless_str_equal(p, "/foo"); + + fail_if(getcwd(cwd, PATH_MAX) == NULL, "getcwd failed"); + + fail_unless(make_path_absolute(p, PATH_MAX, "foo") == SUCCESS); + snprintf(p2, PATH_MAX, "%s/foo", cwd); + fail_unless_str_equal(p, p2); + + fail_unless(make_path_absolute(p, PATH_MAX, "") == SUCCESS); + fail_unless_str_equal(p, cwd); + + /* on-by-one; 2 = terminating null + path delimeter */ + buf_len = strlen(cwd) + strlen("foo") + 2; + buf = malloc(buf_len); + fail_if(buf == NULL); + fail_unless(make_path_absolute(buf, buf_len, "foo") == SUCCESS); + free(buf); +} +END_TEST + +START_TEST(test_make_path_absolute_neg) +{ + char small[1]; + char cwd[PATH_MAX]; + char *small2; + int small_len; + + fail_unless(make_path_absolute(small, 1, "/foo") == ENOBUFS); + fail_unless(make_path_absolute(NULL, 1, "/foo") == ENOBUFS); + + /* off-by-one */ + fail_if(getcwd(cwd, PATH_MAX) == NULL, "getcwd failed"); + small_len = strlen(cwd) + strlen("foo") + 1; + small2 = malloc(small_len); + fail_if(small2 == NULL); + fail_unless(make_path_absolute(small2, small_len, "foo") == ENOBUFS); + free(small2); + + /* just enough space for cwd */ + small_len = strlen(cwd) + 1; + small2 = malloc(small_len); + fail_if(small2 == NULL); + fail_unless(make_path_absolute(small2, small_len, "foo") == ENOBUFS); + free(small2); +} +END_TEST + +/**** make_normalized_absolute_path ****/ +START_TEST(test_make_normalized_absolute_path) +{ + char p[PATH_MAX]; + char p2[PATH_MAX]; + char cwd[PATH_MAX]; + + fail_if(getcwd(cwd, PATH_MAX) == NULL, "getcwd failed"); + + fail_unless(make_normalized_absolute_path(p, PATH_MAX, "foo/baz/../bar") == SUCCESS); + snprintf(p2, PATH_MAX, "%s/foo/bar", cwd); + fail_unless_str_equal(p, p2); + + fail_unless(make_normalized_absolute_path(p, PATH_MAX, "/foo/../bar") == SUCCESS); + fail_unless_str_equal(p, "/bar"); + + fail_unless(make_normalized_absolute_path(p, PATH_MAX, "/foo/../baz/../bar") == SUCCESS); + fail_unless_str_equal(p, "/bar"); +} +END_TEST + +START_TEST(test_make_normalized_absolute_path_neg) +{ + char small[1]; + + fail_unless(make_path_absolute(small, 1, "/foo") == ENOBUFS); + fail_unless(make_path_absolute(NULL, 1, "/foo") == ENOBUFS); +} +END_TEST + +/**** split_path ****/ +START_TEST(test_split_path) +{ + char **array; + int n; + + array = split_path("/foo/bar", &n); + fail_if(array == NULL); + fail_unless(n == 3); + if (array) { + fail_unless_str_equal(array[0], "/"); + fail_unless_str_equal(array[1], "foo"); + fail_unless_str_equal(array[2], "bar"); + free(array); + } + + array = split_path("/foo/../bar", &n); + fail_if(array == NULL); + fail_unless(n == 4); + if (array) { + fail_unless_str_equal(array[0], "/"); + fail_unless_str_equal(array[1], "foo"); + fail_unless_str_equal(array[2], ".."); + fail_unless_str_equal(array[3], "bar"); + free(array); + } + + array = split_path("/foo/bar", NULL); + fail_if(array == NULL); + if (array) { + fail_unless_str_equal(array[0], "/"); + fail_unless_str_equal(array[1], "foo"); + fail_unless_str_equal(array[2], "bar"); + free(array); + } + + array = split_path("foo/bar", &n); + fail_if(array == NULL); + fail_unless(n == 2); + if (array) { + fail_unless_str_equal(array[0], "foo"); + fail_unless_str_equal(array[1], "bar"); + free(array); + } + + array = split_path(".", &n); + fail_if(array == NULL); + fail_unless(n == 1); + if (array) { + fail_unless_str_equal(array[0], "."); + free(array); + } + + array = split_path("foo", &n); + fail_if(array == NULL); + fail_unless(n == 1); + if (array) { + fail_unless_str_equal(array[0], "foo"); + free(array); + } + + /* one might expect { "" } or outright NULL, but we agreed not to + * do changes beyond bugfixes at this point */ + array = split_path("", &n); + fail_if(array == NULL); + fail_unless(n == 0); + if (array) { + fail_unless(array[0] == NULL); + free(array); + } +} +END_TEST + +START_TEST(test_split_path_neg) +{ + char **array; + int n; + + array = split_path(NULL, &n); + fail_unless(array == NULL); + + array = split_path(NULL, NULL); + fail_unless(array == NULL); +} +END_TEST + +/**** normalize_path ****/ +START_TEST(test_normalize_path) +{ + char norm[PATH_MAX]; + char small[8]; + + fail_unless(normalize_path(norm, PATH_MAX, "/foo/../bar") == SUCCESS); + fail_unless_str_equal(norm, "/bar"); + + fail_unless(normalize_path(norm, PATH_MAX, "/foo/../baz/../bar") == SUCCESS); + fail_unless_str_equal(norm, "/bar"); + + fail_unless(normalize_path(norm, PATH_MAX, "foo/baz/../bar") == SUCCESS); + fail_unless_str_equal(norm, "foo/bar"); + + fail_unless(normalize_path(norm, PATH_MAX, "/foo/./bar") == SUCCESS); + fail_unless_str_equal(norm, "/foo/bar"); + + fail_unless(normalize_path(norm, PATH_MAX, "/foo//bar") == SUCCESS); + fail_unless_str_equal(norm, "/foo/bar"); + + fail_unless(normalize_path(norm, PATH_MAX, "/foo//bar") == SUCCESS); + fail_unless_str_equal(norm, "/foo/bar"); + + fail_unless(normalize_path(norm, PATH_MAX, "") == SUCCESS); + fail_unless_str_equal(norm, "."); + + fail_unless(normalize_path(norm, PATH_MAX, "/../..") == SUCCESS); + fail_unless_str_equal(norm, "/"); + + /* on-by-one */ + fail_unless(normalize_path(small, 8, "foo/baz/../bar") == SUCCESS); + fail_unless_str_equal(small, "foo/bar"); +} +END_TEST + +START_TEST(test_normalize_path_neg) +{ + char norm[PATH_MAX]; + char small[4]; + + fail_unless(normalize_path(norm, PATH_MAX, "foo/../..") == PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED); + + /* with a buffer of 4 chars, this would test off-by-one error */ + fail_unless(normalize_path(small, 4, "/foo/../bar") == ENOBUFS); +} +END_TEST + +/**** common_path_prefix ****/ +START_TEST(test_common_path_prefix) +{ + char common[PATH_MAX]; + char small[5]; + int count; + + fail_unless(common_path_prefix(common, PATH_MAX, &count, "/usr/lib", "/usr/share") == SUCCESS); + fail_unless_str_equal(common, "/usr"); + fail_unless(count == 2); + + fail_unless(common_path_prefix(common, PATH_MAX, NULL, "/usr/lib", "/usr/share") == SUCCESS); + fail_unless_str_equal(common, "/usr"); + + fail_unless(common_path_prefix(common, PATH_MAX, &count, "/usr/lib", "/usr/lab") == SUCCESS); + fail_unless_str_equal(common, "/usr"); + fail_unless(count == 2); + + fail_unless(common_path_prefix(common, PATH_MAX, &count, "foo", "bar") == SUCCESS); + fail_unless_str_equal(common, ""); + fail_unless(count == 0); + + fail_unless(common_path_prefix(common, PATH_MAX, &count, "/", "/") == SUCCESS); + fail_unless_str_equal(common, "/"); + fail_unless(count == 1); + + fail_unless(common_path_prefix(common, PATH_MAX, &count, NULL, "/usr/share") == SUCCESS); + fail_unless_str_equal(common, ""); + fail_unless(count == 0); + + /* on-by-one */ + fail_unless(common_path_prefix(small, 5, NULL, "/usr/lib", "/usr/share") == SUCCESS); + fail_unless_str_equal(small, "/usr"); +} +END_TEST + +START_TEST(test_common_path_prefix_neg) +{ + char small[1]; + char small2[4]; + int count; + + fail_unless(common_path_prefix(small, 1, &count, "/usr/lib", "/usr/share") == ENOBUFS); + fail_unless(common_path_prefix(NULL, PATH_MAX, &count, "/usr/lib", "/usr/share") == ENOBUFS); + /* off-by-one */ + fail_unless(common_path_prefix(small2, 4, NULL, "/usr/lib", "/usr/share") == ENOBUFS); +} +END_TEST + +/**** find_existing_directory_ancestor ****/ +START_TEST(test_find_existing_directory_ancestor) +{ + char p[PATH_MAX]; + char cwd[PATH_MAX]; + + fail_unless(find_existing_directory_ancestor(p, PATH_MAX, "/etc/passwd") == SUCCESS); + fail_unless_str_equal(p, "/etc"); + + /* if pathname does not contain a slash, the parent is cwd */ + fail_if(getcwd(cwd, PATH_MAX) == NULL, "getcwd failed"); + + fail_unless(find_existing_directory_ancestor(p, PATH_MAX, "foo/bar") == SUCCESS); + fail_unless_str_equal(p, cwd); +} +END_TEST + +START_TEST(test_find_existing_directory_ancestor_neg) +{ + char small[4]; + fail_unless(find_existing_directory_ancestor(small, 4, "/etc/passwd") == ENOBUFS); + fail_unless(find_existing_directory_ancestor(NULL, 4, "/etc/passwd") == ENOBUFS); +} +END_TEST + +/**** directory_list ****/ +static void setup_directory_list(void) +{ + char *s = NULL; + int ret; + + s = strdup(DIR_TEMPLATE); + fail_unless(s != NULL, "strdup failed\n"); + if (!s) return; + dlist_dir = mkdtemp(s); + fail_unless(dlist_dir != NULL, "mkstemp failed [%d][%s]", errno, strerror(errno)); + + ret = asprintf(&dlist_subdir, "%s/%s", dlist_dir, SUBDIR); + fail_unless(ret != 1, "strdup failed\n"); + ret = mkdir(dlist_subdir, 0700); + fail_unless(ret != -1, "mkdir %s failed [%d][%s]", dlist_subdir, errno, strerror(errno)); + + ret = asprintf(&dlist_subsubdir, "%s/%s", dlist_subdir, SUBSUBDIR); + fail_unless(ret != 1, "strdup failed\n"); + ret = mkdir(dlist_subsubdir, 0700); + fail_unless(ret != -1, "mkdir %s failed [%d][%s]", dlist_subsubdir, errno, strerror(errno)); +} + +static void teardown_directory_list(void) +{ + int ret; + + if (dlist_subsubdir) { + ret = rmdir(dlist_subsubdir); + fail_unless(ret != -1, "unlink %s failed [%d][%s]", dlist_subsubdir, errno, strerror(errno)); + free(dlist_subsubdir); + dlist_subsubdir = NULL; + } + + if (dlist_subdir) { + ret = rmdir(dlist_subdir); + fail_unless(ret != -1, "unlink %s failed [%d][%s]", dlist_subdir, errno, strerror(errno)); + free(dlist_subdir); + dlist_subdir = NULL; + } + + if (dlist_dir) { + ret = rmdir(dlist_dir); + fail_unless(ret != -1, "unlink %s failed [%d][%s]", dlist_dir, errno, strerror(errno)); + free(dlist_dir); + dlist_dir = NULL; + } +} + +static bool dirlist_cb_nonrecursive(const char *directory, + const char *base_name, + const char *path, + struct stat *info, + void *user_data) +{ + int *data = (int *) user_data; + + fail_unless_str_equal(path, dlist_subdir); + fail_unless(*data == 123); + + return true; +} + +static bool dirlist_cb_recursive(const char *directory, const char *base_name, + const char *path, struct stat *info, + void *user_data) +{ + bool *seen_child = (bool *) user_data; + static bool seen_parent = false; + + if (!seen_parent) { + fail_unless_str_equal(path, dlist_subdir); + seen_parent = true; + } else { + *seen_child = true; + fail_unless_str_equal(path, dlist_subsubdir); + seen_parent = false; + } + + return true; +} + +START_TEST(test_directory_list) +{ + int data = 123; + bool seen_child; + + fail_unless(directory_list(dlist_dir, false, dirlist_cb_nonrecursive, &data) == SUCCESS); + + seen_child = false; + fail_unless(directory_list(dlist_dir, true, dirlist_cb_recursive, &seen_child) == SUCCESS); + fail_unless(seen_child == true); + + seen_child = false; + fail_unless(directory_list(dlist_dir, false, dirlist_cb_recursive, &seen_child) == SUCCESS); + fail_unless(seen_child == false); +} +END_TEST + +START_TEST(test_directory_list_neg) +{ + fail_if(directory_list("/not/here", false, dirlist_cb_nonrecursive, NULL) == SUCCESS); + fail_if(directory_list("/etc/passwd", false, dirlist_cb_nonrecursive, NULL) == SUCCESS); +} +END_TEST + +/**** is_ancestor_path ****/ +START_TEST(test_is_ancestor_path) +{ + fail_unless(is_ancestor_path("/a/b/c", "/a/b/c/d") == true); + /* equal, not ancestor */ + fail_unless(is_ancestor_path("/a/b/c/d", "/a/b/c/d") == false); + fail_unless(is_ancestor_path("/a/x/c", "/a/b/c/d") == false); + fail_unless(is_ancestor_path(NULL, "/a/b/c/d") == false); + fail_unless(is_ancestor_path("/a/x/c", NULL) == false); + fail_unless(is_ancestor_path(NULL, NULL) == false); + fail_unless(is_ancestor_path("", "") == false); +} +END_TEST + + +static Suite *path_utils_suite(void) +{ + Suite *s = suite_create("path_utils"); + + TCase *tc_path_utils = tcase_create("path_utils"); + TCase *tc_directory_list = tcase_create("path_utils_directory_list"); + + tcase_add_test(tc_path_utils, test_dirname); + tcase_add_test(tc_path_utils, test_dirname_neg); + + tcase_add_test(tc_path_utils, test_basename); + tcase_add_test(tc_path_utils, test_basename_neg); + + tcase_add_test(tc_path_utils, test_dirname_and_basename); + tcase_add_test(tc_path_utils, test_dirname_and_basename_neg); + + tcase_add_test(tc_path_utils, test_is_absolute_path); + + tcase_add_test(tc_path_utils, test_path_concat); + tcase_add_test(tc_path_utils, test_path_concat_neg); + + tcase_add_test(tc_path_utils, test_split_path); + tcase_add_test(tc_path_utils, test_split_path_neg); + + tcase_add_test(tc_path_utils, test_make_path_absolute); + tcase_add_test(tc_path_utils, test_make_path_absolute_neg); + + tcase_add_test(tc_path_utils, test_normalize_path); + tcase_add_test(tc_path_utils, test_normalize_path_neg); + + tcase_add_test(tc_path_utils, test_make_normalized_absolute_path); + tcase_add_test(tc_path_utils, test_make_normalized_absolute_path_neg); + + tcase_add_test(tc_path_utils, test_common_path_prefix); + tcase_add_test(tc_path_utils, test_common_path_prefix_neg); + + tcase_add_test(tc_path_utils, test_find_existing_directory_ancestor); + tcase_add_test(tc_path_utils, test_find_existing_directory_ancestor_neg); + + tcase_add_test(tc_path_utils, test_is_ancestor_path); + + tcase_add_checked_fixture(tc_directory_list, + setup_directory_list, + teardown_directory_list); + tcase_add_test(tc_directory_list, test_directory_list); + tcase_add_test(tc_directory_list, test_directory_list_neg); + + suite_add_tcase(s, tc_path_utils); + suite_add_tcase(s, tc_directory_list); + + return s; +} + +int main(void) +{ + int number_failed; + + Suite *s = path_utils_suite(); + SRunner *sr = srunner_create(s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/tests/sanity/ref_array_ut.c b/tests/sanity/ref_array_ut.c new file mode 100644 index 0000000..da4b5df --- /dev/null +++ b/tests/sanity/ref_array_ut.c @@ -0,0 +1,775 @@ +/* + REF ARRAY + + Implementation of the dynamic array with reference count. + + Copyright (C) Dmitri Pal 2009 + + 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; either version 3 of the License, or + (at your option) any later version. + 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, see . +*/ + +#include /* for errors */ +#include +#include +#include +#include + +#include "ref_array.h" +#define TRACE_HOME +#include "trace.h" + +int verbose = 0; + +#define RAOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + +typedef int (*test_fn)(void); + +/* Basic test */ +static int ref_array_basic_test(void) +{ + const char *line1 = "line1"; + const char *line2 = "line2"; + const char *line3 = "line3"; + const char *line4 = "line4"; + const char *line5 = "line5"; + const char *line6 = "line6"; + uint32_t i; + struct ref_array *ra; + struct ref_array *ra2; + int error = EOK; + uint32_t len = 0; + uint32_t other_len = 0; + char *ret; + char *elem; + void *ptr; + + error = ref_array_create(&ra, sizeof(char *), 1, NULL, NULL); + if (error) { + printf("Failed to create array %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line1); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 1 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line2); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 2 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line3); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 3 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line4); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 4 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line5); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 5 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line6); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 6 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + RAOUT(printf("\n\nTest 1 - Printing lines.\n\n")); + + error = ref_array_getlen(ra, &other_len); + if (error) { + ref_array_destroy(ra); + printf("Failed to get length %d\n", error); + return error; + } + + len = ref_array_len(ra); + + if (len != other_len) { + ref_array_destroy(ra); + printf("Lengths do not match:\n"); + printf("Len : %d\n", len); + printf("Get Len: %d\n", other_len); + return EFAULT; + } + + for (i = 0; i < len; i++) { + ref_array_get(ra, i, &ret); + RAOUT(printf("%s\n", ret)); + } + + RAOUT(printf("\n\nTest 2 - Creating reference and then printing lines.\n\n")); + + ra2 = ref_array_getref(ra); + ref_array_destroy(ra); + + for (i = 0; i < len; i++) { + ret = *((char **)ref_array_get(ra2, i, NULL)); + RAOUT(printf("%s\n", ret)); + } + + RAOUT(printf("\n\nTest 3 - Get elements with copying.\n\n")); + + for (i = 0; i < len; i++) { + ref_array_get(ra2, i, &ret); + RAOUT(printf("%s\n", ret)); + } + + RAOUT(printf("\n\nTest 4a - Get elements with copying and assignment.\n\n")); + + /* This is a bad practice to use one variable + * as a parameter and as an acceptor for the return value. + * See next example for a better way to do it. + */ + for (i = 0; i < len; i++) { + ret = *((char **)ref_array_get(ra2, i, &ret)); + RAOUT(printf("%s\n", ret)); + } + + RAOUT(printf("\n\nTest 4b - Get elements with copying and assignment.\n\n")); + + for (i = 0; i < len; i++) { + ret = *((char **)ref_array_get(ra2, i, &elem)); + RAOUT(printf("%s\n", ret)); + RAOUT(printf("%s\n", elem)); + if (strcmp(ret, elem) != 0) { + ref_array_destroy(ra2); + printf("\nRetrieved strings were expected to be same,\n"); + printf("but they are not:\n"); + printf("By pointer:[%s]\nAs element:[%s]\n", ret, elem); + return EFAULT; + } + } + + RAOUT(printf("\n\nTest 5 - While loop up.\n\n")); + + i = 0; + for (;;) { + ptr = ref_array_get(ra2, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + RAOUT(printf("\n\nTest 6 - While loop down.\n\n")); + + i = len - 1; + for (;;) { + ptr = ref_array_get(ra2, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i--; + } + else break; + } + + RAOUT(printf("\n\nDone!!!\n\n")); + + ref_array_destroy(ra2); + return EOK; +} + +static void array_cleanup(void *elem, + ref_array_del_enum type, + void *data) +{ + RAOUT(printf("%s%s\n", (char *)data, *((char **)elem))); + free(*((char **)elem)); +} + +/* Free test */ +static int ref_array_free_test(void) +{ + const char *line1 = "line1"; + const char *line2 = "line2"; + const char *line3 = "line3"; + const char *line4 = "line4"; + char text[] = "Deleting: "; + char *str; + uint32_t i; + struct ref_array *ra; + int error = EOK; + char *ret; + void *ptr; + + error = ref_array_create(&ra, sizeof(char *), 1, array_cleanup, (char *)text); + if (error) { + printf("Failed to create array %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + str = strdup(line1); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 1 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + str = strdup(line2); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 2 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + str = strdup(line3); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 3 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + str = strdup(line4); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 4 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + RAOUT(printf("\n\nDone!!!\n\n")); + + ref_array_destroy(ra); + return EOK; +} + +static int ref_array_adv_test(void) +{ + int error = EOK; + const char *lines[] = { "line0", + "line1", + "line2", + "line3", + "line4", + "line5", + "line6", + "line7", + "line8", + "line9" }; + char text[] = "Deleting: "; + char *str; + uint32_t i; + struct ref_array *ra; + char *ret; + void *ptr; + int expected[] = { 0, 1, 7, 8, 9 }; + int expected2[] = { 1, 7, 8, 9, 0 }; + + error = ref_array_create(&ra, + sizeof(char *), + 1, + array_cleanup, + (char *)text); + if (error) { + printf("Failed to create array %d\n", error); + return error; + } + + for (i = 0; i < 5;i++) { + + str = strdup(lines[i]); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append line %d, error %d\n", + i, error); + return error; + } + } + + RAOUT(printf("\nInitial array.\n")); + + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + + /* Try to remove invalid entry */ + error = ref_array_remove(ra, 1000); + if (error != ERANGE) { + ref_array_destroy(ra); + printf("Removing entry expected error got success.\n"); + return -1; + } + + /* Try to insert invalid entry */ + error = ref_array_insert(ra, 1000, &text); + if (error != ERANGE) { + ref_array_destroy(ra); + printf("Inserting entry expected error got success.\n"); + return -1; + } + + /* Try to replace invalid entry */ + error = ref_array_replace(ra, 1000, &text); + if (error != ERANGE) { + ref_array_destroy(ra); + printf("Replacing entry expected error got success.\n"); + return -1; + } + + /* Insert several entries */ + for (i = 9; i > 4; i--) { + + str = strdup(lines[i]); + + error = ref_array_insert(ra, 9 - i, &str); + if (error) { + ref_array_destroy(ra); + free(str); + printf("Failed to insert line %d, error %d\n", + i, error); + return error; + } + } + + /* Displpay array contents */ + RAOUT(printf("\nArray with inserted values.\n")); + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + /* Replace everything */ + for (i = 0; i < 10;i++) { + + str = strdup(lines[i]); + + error = ref_array_replace(ra, i, &str); + if (error) { + ref_array_destroy(ra); + free(str); + printf("Failed to replace line %d, error %d\n", + i, error); + return error; + } + } + + /* Displpay array contents */ + RAOUT(printf("\nArray with replaced values.\n")); + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + /* Reset */ + ref_array_reset(ra); + + /* Displpay array contents */ + RAOUT(printf("\nEmpty array.\n")); + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + /* Add everything */ + for (i = 0; i < 10;i++) { + + str = strdup(lines[i]); + + error = ref_array_insert(ra, i, &str); + if (error) { + ref_array_destroy(ra); + free(str); + printf("Failed to insert into array %d\n", error); + return error; + } + } + + /* Displpay array contents */ + RAOUT(printf("\nAll added back.\n")); + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + /* Remove part */ + for (i = 0; i < 5;i++) { + + error = ref_array_remove(ra, 2); + if (error) { + ref_array_destroy(ra); + printf("Failed to remive item from array %d\n", error); + return error; + } + } + + /* Displpay array contents */ + RAOUT(printf("\nCleaned array.\n")); + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + RAOUT(printf("\n\nChecking for expected contents\n\n")); + + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("Comparing:\n[%s]\n[%s]\n\n", + ret, lines[expected[i]])); + if (strcmp(ret, lines[expected[i]]) != 0) { + printf("Unexpected contents of the array.\n"); + ref_array_destroy(ra); + return -1; + } + i++; + } + else break; + } + + RAOUT(printf("\n\nSwap test\n\n")); + + if ((error = ref_array_swap(ra, 0, 1)) || + (error = ref_array_swap(ra, 1, 2)) || + (error = ref_array_swap(ra, 2, 3)) || + (error = ref_array_swap(ra, 3, 4))) { + ref_array_destroy(ra); + printf("Failed to to swap %d\n", error); + return error; + } + + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("Comparing:\n[%s]\n[%s]\n\n", + ret, lines[expected2[i]])); + if (strcmp(ret, lines[expected2[i]]) != 0) { + printf("Unexpected contents of the array.\n"); + ref_array_destroy(ra); + return -1; + } + i++; + } + else break; + } + + RAOUT(printf("\n\nDone!!!\n\n")); + + ref_array_destroy(ra); + return EOK; +} + +static int copy_cb(void *elem, + void *new_elem) +{ + char *ne = NULL; + + ne = strdup(*((char **)elem)); + *((char **)new_elem) = ne; + + RAOUT(printf("Source: %s\nCopy:%s\n", *((char **)elem), ne)); + + + return EOK; +} + +static int ref_array_copy_test(void) +{ + const char *line1 = "line1"; + const char *line2 = "line2"; + const char *line3 = "line3"; + const char *line4 = "line4"; + const char *line5 = "line5"; + const char *line6 = "line6"; + uint32_t i; + struct ref_array *ra; + struct ref_array *ra2; + int error = EOK; + uint32_t len = 6; + char text[] = "Deleting: "; + + error = ref_array_create(&ra, sizeof(char *), 1, NULL, NULL); + if (error) { + printf("Failed to create array %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line1); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 1 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line2); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 2 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line3); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 3 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line4); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 4 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line5); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 5 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + error = ref_array_append(ra, &line6); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 6 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 0)); + + RAOUT(printf("\n\nCopy lines.\n\n")); + + error = ref_array_copy(ra, copy_cb, array_cleanup, (char *)text, &ra2); + if (error) { + ref_array_destroy(ra); + printf("Failed to copy array %d\n", error); + return error; + } + + for (i = 0; i < len; i++) { + if (strcmp(*((char **)ref_array_get(ra, i, NULL)), + *((char **)ref_array_get(ra2, i, NULL))) != 0) { + printf("\nRetrieved strings were expected to be same,\n"); + printf("but they are not:\n"); + printf("First:[%s]\nSecond:[%s]\n", + *((char **)ref_array_get(ra, i, NULL)), + *((char **)ref_array_get(ra2, i, NULL))); + ref_array_destroy(ra); + ref_array_destroy(ra2); + return EFAULT; + } + } + + RAOUT(printf("\n\nSource array.\n\n")); + RAOUT(ref_array_debug(ra, 0)); + ref_array_destroy(ra); + + RAOUT(printf("\n\nAbout to destroy a copy.\n\n")); + RAOUT(ref_array_debug(ra2, 0)); + ref_array_destroy(ra2); + + RAOUT(printf("\n\nDone!!!\n\n")); + return EOK; + +} + +static int ref_array_copy_num_test(void) +{ + uint32_t i,j,k; + struct ref_array *ra; + struct ref_array *ra2; + int error = EOK; + uint32_t len = 5; + + error = ref_array_create(&ra, sizeof(uint32_t), 1, NULL, NULL); + if (error) { + printf("Failed to create array %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 1)); + + for (i = 0; i < len; i++) { + error = ref_array_append(ra, &i); + if (error) { + ref_array_destroy(ra); + printf("Failed to append number to array %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra, 1)); + } + + RAOUT(printf("\n\nCopy num test.\n\n")); + + error = ref_array_copy(ra, NULL, NULL, NULL, &ra2); + if (error) { + ref_array_destroy(ra); + printf("Failed to copy array %d\n", error); + return error; + } + + for (i = 0; i < len; i++) { + j = *((uint32_t *)(ref_array_get(ra, i, NULL))); + k = *((uint32_t *)(ref_array_get(ra2, i, NULL))); + if (j != k) { + printf("\nRetrieved values were expected to be same,\n"); + printf("but they are not:\n"); + printf("First:[%d]\nSecond:[%d]\n", j, k); + ref_array_destroy(ra); + ref_array_destroy(ra2); + return EFAULT; + } + } + + ref_array_destroy(ra); + ref_array_destroy(ra2); + + RAOUT(printf("\n\nDone!!!\n\n")); + return EOK; + +} + +/* Main function of the unit test */ +int main(int argc, char *argv[]) +{ + int error = 0; + test_fn tests[] = { ref_array_basic_test, + ref_array_free_test, + ref_array_adv_test, + ref_array_copy_test, + ref_array_copy_num_test, + NULL }; + test_fn t; + int i = 0; + char *var; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + else { + var = getenv("COMMON_TEST_VERBOSE"); + if (var) verbose = 1; + } + + RAOUT(printf("Start\n")); + + while ((t = tests[i++])) { + error = t(); + if (error) { + RAOUT(printf("Failed with error %d!\n", error)); + return error; + } + } + + RAOUT(printf("Success!\n")); + return 0; +} diff --git a/tests/sanity/simplebuffer_ut.c b/tests/sanity/simplebuffer_ut.c new file mode 100644 index 0000000..8a11123 --- /dev/null +++ b/tests/sanity/simplebuffer_ut.c @@ -0,0 +1,145 @@ +/* + Simple buffer UNIT test + + Basic buffer manipulation routines. + + Copyright (C) Dmitri Pal 2010 + + 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; either version 3 of the License, or + (at your option) any later version. + 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, see . +*/ + +#include +#include +#include +#include +#include +#include +#define TRACE_HOME +#include "trace.h" +#include "simplebuffer.h" + + +int verbose = 0; + +#define BOOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + + +static int simple_test(void) +{ + int error = EOK; + struct simplebuffer *data = NULL; + char str1[] = "test string 1"; + char str2[] = "test string 2"; + const char str3[] = "test string 3"; + uint32_t left = 0; + int i; + const unsigned char *buf; + + BOOUT(printf("Simple test start.\n")); + + error = simplebuffer_alloc(&data); + if (error) { + printf("Failed to allocate object %d\n", error); + return error; + } + + error = simplebuffer_add_raw(data, + (void *)str1, + strlen(str1), + 1); + if (error) { + printf("Failed to add string to an object %d\n", error); + simplebuffer_free(data); + return error; + } + + error = simplebuffer_add_cr(data); + if (error) { + printf("Failed to add CR to an object %d\n", error); + simplebuffer_free(data); + return error; + } + + error = simplebuffer_add_raw(data, + (void *)str2, + strlen(str2), + 1); + if (error) { + printf("Failed to add string to an object %d\n", error); + simplebuffer_free(data); + return error; + } + + error = simplebuffer_add_cr(data); + if (error) { + printf("Failed to add CR to an object %d\n", error); + simplebuffer_free(data); + return error; + } + + error = simplebuffer_add_str(data, + str3, + strlen(str3), + 1); + if (error) { + printf("Failed to add string to an object %d\n", error); + simplebuffer_free(data); + return error; + } + + left = simplebuffer_get_len(data); + buf = simplebuffer_get_buf(data); + + BOOUT(for(i = 0; i < left; i++) { + printf("%02d: %02X\n", i, buf[i]); + }); + + if (verbose) { + while (left > 0) { + error = simplebuffer_write(1, data, &left); + if (error) { + printf("Failed to write to output %d\n", error); + simplebuffer_free(data); + return error; + } + } + } + + BOOUT(printf("\n[%s]\n", simplebuffer_get_buf(data))); + BOOUT(printf("Length: %d\n", simplebuffer_get_len(data))); + + + simplebuffer_free(data); + + BOOUT(printf("Simple test end.\n")); + return error; +} + +int main(int argc, char *argv[]) +{ + int error = EOK; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + + BOOUT(printf("Start\n")); + + if ((error = simple_test())) { + printf("Test failed! Error %d.\n", error); + return -1; + } + + BOOUT(printf("Success!\n")); + return 0; +} diff --git a/tests/sanity/trace.h b/tests/sanity/trace.h new file mode 100644 index 0000000..ea85985 --- /dev/null +++ b/tests/sanity/trace.h @@ -0,0 +1,175 @@ +/* + COMMON TRACE + + Common header file for tracing. + + Copyright (C) Dmitri Pal 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + 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 Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef COMMON_TRACE_H +#define COMMON_TRACE_H + +#ifdef TRACE_LEVEL +#define HAVE_TRACE +#include + +/* The trace level is a bit mask */ +#define TRACE_FLOW 0x0000001 /* - trace messages that are entry exit into functions */ +#define TRACE_ERROR 0x0000002 /* - trace messages that are errors */ +#define TRACE_INFO 0x0000004 /* - trace things that are informational */ + + +#ifdef TRACE_HOME /* Define this in the module that contains main */ +unsigned trace_level = TRACE_LEVEL; +#else +extern unsigned trace_level; +#endif /* TRACE_HOME */ +#endif /* TRACE_LEVEL */ + + + +#ifdef HAVE_TRACE +/* Tracing strings */ +#define TRACE_STRING(level, msg, str) \ + do { \ + if (level & trace_level) { \ + printf("[DEBUG] %40s (%4d) %s%s %s\n", \ + __FILE__, __LINE__, \ + (level == TRACE_ERROR) ? "ERROR-> " : "", \ + (msg != NULL) ? msg : "MISSING MESSAGE", \ + (str != NULL) ? str : "(null)"); \ + } \ + } while(0) + +/* Tracing unsigned numbers */ +#define TRACE_NUMBER(level, msg, num) \ + do { \ + if (level & trace_level) { \ + printf("[DEBUG] %40s (%4d) %s%s %lu\n", \ + __FILE__, __LINE__, \ + (level == TRACE_ERROR) ? "ERROR-> " : "", \ + (msg != NULL) ? msg : "MISSING MESSAGE", \ + (unsigned long int)(num)); \ + } \ + } while(0) + +/* Tracing signed numbers */ +#define TRACE_SNUMBER(level, msg, num) \ + do { \ + if (level & trace_level) { \ + printf("[DEBUG] %40s (%4d) %s%s %ld\n", \ + __FILE__, __LINE__, \ + (level == TRACE_ERROR) ? "ERROR-> " : "", \ + (msg != NULL) ? msg : "MISSING MESSAGE", \ + (long int)(num)); \ + } \ + } while(0) + +/* Tracing long numbers */ +#define TRACE_LNUMBER(level, msg, num) \ + do { \ + if (level & trace_level) { \ + printf("[DEBUG] %40s (%4d) %s%s %llu\n", \ + __FILE__, __LINE__, \ + (level == TRACE_ERROR) ? "ERROR-> " : "", \ + (msg != NULL) ? msg : "MISSING MESSAGE", \ + (unsigned long long int)(num)); \ + } \ + } while(0) + +/* Tracing signed long numbers */ +#define TRACE_SLNUMBER(level, msg, num) \ + do { \ + if (level & trace_level) { \ + printf("[DEBUG] %40s (%4d) %s%s %lld\n", \ + __FILE__, __LINE__, \ + (level == TRACE_ERROR) ? "ERROR-> " : "", \ + (msg != NULL) ? msg : "MISSING MESSAGE", \ + (long long int)(num)); \ + } \ + } while(0) + +/* Tracing doubles */ +#define TRACE_DOUBLE(level, msg, num) \ + do { \ + if (level & trace_level) { \ + printf("[DEBUG] %40s (%4d) %s%s %e\n", \ + __FILE__, __LINE__, \ + (level == TRACE_ERROR) ? "ERROR-> " : "", \ + (msg != NULL) ? msg : "MISSING MESSAGE", \ + (double)(num)); \ + } \ + } while(0) + +#define TRACE_RETURN(flow, val) \ + do { \ + char mstr[200]; \ + sprintf(mstr, "%s returning:", __FUNCTION__); \ + flow(mstr, val); \ + } while(0) + + +/* Assertion */ +#define TRACE_ASSERT(expression) expression ? : printf("ASSERTION FAILED\n") +#else /* HAVE_TRACE */ + +/* Noop in case the tracing is disabled */ +#define TRACE_STRING(level, msg, str) +#define TRACE_NUMBER(level, msg, num) +#define TRACE_SNUMBER(level, msg, num) +#define TRACE_LNUMBER(level, msg, num) +#define TRACE_SLNUMBER(level, msg, num) +#define TRACE_DOUBLE(level, msg, num) +#define TRACE_RETURN(flow, val) +#define TRACE_ASSERT(expression) +#endif /* HAVE_TRACE */ + + +/* Convenience wrappers for strings */ +#define TRACE_FLOW_STRING(msg, str) TRACE_STRING(TRACE_FLOW, msg, str) +#define TRACE_ERROR_STRING(msg, str) TRACE_STRING(TRACE_ERROR, msg, str) +#define TRACE_INFO_STRING(msg, str) TRACE_STRING(TRACE_INFO, msg, str) + +/* Convenience wrappers for unsigned numbers */ +#define TRACE_FLOW_NUMBER(msg, num) TRACE_NUMBER(TRACE_FLOW, msg, num) +#define TRACE_ERROR_NUMBER(msg, num) TRACE_NUMBER(TRACE_ERROR, msg, num) +#define TRACE_INFO_NUMBER(msg, num) TRACE_NUMBER(TRACE_INFO, msg, num) + +/* Convenience wrappers for signed numbers */ +#define TRACE_FLOW_SNUMBER(msg, num) TRACE_SNUMBER(TRACE_FLOW, msg, num) +#define TRACE_ERROR_SNUMBER(msg, num) TRACE_SNUMBER(TRACE_ERROR, msg, num) +#define TRACE_INFO_SNUMBER(msg, num) TRACE_SNUMBER(TRACE_INFO, msg, num) + +/* Convenience wrappers for 64-bit long unsigned numbers */ +#define TRACE_FLOW_LNUMBER(msg, num) TRACE_LNUMBER(TRACE_FLOW, msg, num) +#define TRACE_ERROR_LNUMBER(msg, num) TRACE_LNUMBER(TRACE_ERROR, msg, num) +#define TRACE_INFO_LNUMBER(msg, num) TRACE_LNUMBER(TRACE_INFO, msg, num) + +/* Convenience wrappers for 64-bit long signed numbers */ +#define TRACE_FLOW_SLNUMBER(msg, num) TRACE_SLNUMBER(TRACE_FLOW, msg, num) +#define TRACE_ERROR_SLNUMBER(msg, num) TRACE_SLNUMBER(TRACE_ERROR, msg, num) +#define TRACE_INFO_SLNUMBER(msg, num) TRACE_SLNUMBER(TRACE_INFO, msg, num) + +/* Convenience wrappers for numbers */ +#define TRACE_FLOW_DOUBLE(msg, num) TRACE_DOUBLE(TRACE_FLOW, msg, num) +#define TRACE_ERROR_DOUBLE(msg, num) TRACE_DOUBLE(TRACE_ERROR, msg, num) +#define TRACE_INFO_DOUBLE(msg, num) TRACE_DOUBLE(TRACE_INFO, msg, num) + +/* Some other nice wrappers for function entry and exit */ +#define TRACE_FLOW_ENTRY() TRACE_FLOW_STRING(__FUNCTION__, "Entry") +#define TRACE_FLOW_EXIT() TRACE_FLOW_STRING(__FUNCTION__, "Exit") +#define TRACE_FLOW_RETURN(val) TRACE_RETURN(TRACE_FLOW_NUMBER, val) + + +#endif /* COMMON_TRACE_H */ diff --git a/tests/tests.yml b/tests/tests.yml new file mode 100644 index 0000000..8e41935 --- /dev/null +++ b/tests/tests.yml @@ -0,0 +1,28 @@ +- hosts: localhost + roles: + - role: standard-test-basic + tags: + - classic + tests: + - sanity: + dir: sanity + run: ./ding-libs-tests.sh + required_packages: + - check + - check-devel + - libbasicobjects + - libbasicobjects-devel + - libref_array + - libref_array-devel + - libcollection + - libcollection-devel + - libdhash + - libdhash-devel + - libconfig + - libconfig-devel + - libini_config + - libini_config-devel + - libpath_utils + - libpath_utils-devel + - make + - gcc