diff options
Diffstat (limited to 'lib/ldb/tests/ldb_msg.c')
-rw-r--r-- | lib/ldb/tests/ldb_msg.c | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/lib/ldb/tests/ldb_msg.c b/lib/ldb/tests/ldb_msg.c new file mode 100644 index 0000000..31786a9 --- /dev/null +++ b/lib/ldb/tests/ldb_msg.c @@ -0,0 +1,380 @@ +/* + * from cmocka.c: + * These headers or their equivalents should be included prior to + * including + * this header file. + * + * #include <stdarg.h> + * #include <stddef.h> + * #include <setjmp.h> + * + * This allows test applications to use custom definitions of C standard + * library functions and types. + */ +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include <errno.h> +#include <unistd.h> +#include <talloc.h> + +#include <ldb.h> +#include <ldb_private.h> +#include <string.h> +#include <ctype.h> + +struct test_ctx { + struct ldb_message *msg; +}; + +static int ldb_msg_setup(void **state) +{ + struct test_ctx *test_ctx; + + test_ctx = talloc_zero(NULL, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->msg = ldb_msg_new(test_ctx); + + *state = test_ctx; + return 0; +} + +static int ldb_msg_teardown(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, + struct test_ctx); + + talloc_free(test_ctx); + return 0; +} + + +static void add_uint_value(struct test_ctx *test_ctx, + struct ldb_message *msg, + const char *attr, + unsigned int x) +{ + int ret; + struct ldb_val v, v_dup; + char s[5]; + snprintf(s, sizeof(s), "%04x", x); + v.data = (uint8_t *)s; + v.length = 4; + v_dup = ldb_val_dup(test_ctx, &v); + assert_non_null(v_dup.data); + assert_ptr_not_equal(v_dup.data, v.data); + assert_int_equal(v_dup.length, 4); + + ret = ldb_msg_add_value(msg, attr, &v_dup, NULL); + assert_int_equal(ret, LDB_SUCCESS); +} + + +static void test_ldb_msg_find_duplicate_val(void **state) +{ + int ret; + unsigned int i; + struct test_ctx *test_ctx = talloc_get_type_abort(*state, + struct test_ctx); + struct ldb_message *msg = test_ctx->msg; + struct ldb_message_element *el; + struct ldb_val dummy; + struct ldb_val *dupe = &dummy; /* so we can tell it was modified to NULL, not left as NULL */ + + ret = ldb_msg_add_empty(msg, "el1", 0, &el); + assert_int_equal(ret, LDB_SUCCESS); + + /* An empty message contains no duplicates */ + ret = ldb_msg_find_duplicate_val(NULL, test_ctx, el, &dupe, 0); + assert_int_equal(ret, LDB_SUCCESS); + assert_null(dupe); + + for (i = 0; i < 5; i++) { + add_uint_value(test_ctx, msg, "el1", i); + } + /* at this point there are no duplicates, and the check uses the naive + quadratic path */ + ret = ldb_msg_find_duplicate_val(NULL, test_ctx, el, &dupe, 0); + assert_int_equal(ret, LDB_SUCCESS); + assert_null(dupe); + + /* add a duplicate, still using quadratric path */ + add_uint_value(test_ctx, msg, "el1", 3); + ret = ldb_msg_find_duplicate_val(NULL, test_ctx, el, &dupe, 0); + assert_int_equal(ret, LDB_SUCCESS); + assert_non_null(dupe); + assert_int_equal(dupe->length, 4); + assert_memory_equal(dupe->data, "0003", 4); + + /* add some more, triggering algorithmic jump */ + for (i = 2; i < 11; i++) { + add_uint_value(test_ctx, msg, "el1", i); + } + ret = ldb_msg_find_duplicate_val(NULL, test_ctx, el, &dupe, 0); + assert_int_equal(ret, LDB_SUCCESS); + assert_non_null(dupe); + assert_int_equal(dupe->length, 4); + /*XXX not really guaranteed by the API */ + assert_memory_equal(dupe->data, "0002", 4); + + /* start a new element without duplicates, for the clever algorithm */ + ldb_msg_add_empty(msg, "el2", 0, &el); + for (i = 0; i < 12; i++) { + add_uint_value(test_ctx, msg, "el2", i); + } + ret = ldb_msg_find_duplicate_val(NULL, test_ctx, el, &dupe, 0); + assert_int_equal(ret, LDB_SUCCESS); + assert_null(dupe); +} + + +static struct ldb_message_element *new_msg_element(TALLOC_CTX *mem_ctx, + const char *name, + unsigned int value_offset, + unsigned int num_values) +{ + unsigned int i, x; + struct ldb_message_element *el = talloc_zero(mem_ctx, + struct ldb_message_element); + + el->values = talloc_array(el, struct ldb_val, num_values); + for (i = 0; i < num_values; i++) { + struct ldb_val v; + char s[50]; + v.data = (uint8_t *)s; + /* % 3 is to ensure the values list is unsorted */ + x = i + value_offset; + v.length = snprintf(s, sizeof(s), "%u %u", x % 3, x); + el->values[i] = ldb_val_dup(mem_ctx, &v); + } + el->name = name; + el->num_values = num_values; + return el; +} + +static void _assert_element_equal(struct ldb_message_element *a, + struct ldb_message_element *b, + const char * const file, + const int line) +{ + unsigned int i; + _assert_int_equal(a->num_values, b->num_values, file, line); + _assert_int_equal(a->flags, b->flags, file, line); + _assert_string_equal(a->name, b->name, file, line); + for (i = 0; i < a->num_values; i++) { + struct ldb_val *v1 = &a->values[i]; + struct ldb_val *v2 = &b->values[i]; + _assert_int_equal(v1->length, v2->length, file, line); + _assert_memory_equal(v1->data, v2->data, v1->length, + file, line); + } +} + +#define assert_element_equal(a, b) \ + _assert_element_equal((a), (b), \ + __FILE__, __LINE__) + + +static void test_ldb_msg_find_common_values(void **state) +{ + /* we only use the state as a talloc context */ + struct ldb_message_element *el, *el2, *el3, *el4, *el2b, *empty; + struct ldb_message_element *orig, *orig2, *orig3, *orig4; + int ret; + const uint32_t remove_dupes = LDB_MSG_FIND_COMMON_REMOVE_DUPLICATES; + el = new_msg_element(*state, "test", 0, 4); + el2 = new_msg_element(*state, "test", 4, 4); + el3 = new_msg_element(*state, "test", 6, 4); + empty = new_msg_element(*state, "test", 0, 0); + orig = new_msg_element(*state, "test", 0, 4); + orig2 = new_msg_element(*state, "test", 4, 4); + orig3 = new_msg_element(*state, "test", 6, 4); + + /* first round is with short value arrays, using quadratic method */ + /* we expect no collisions here */ + ret = ldb_msg_find_common_values(NULL, *state, el, el2, 0); + assert_int_equal(ret, LDB_SUCCESS); + + /*or here */ + ret = ldb_msg_find_common_values(NULL, *state, el, el3, 0); + assert_int_equal(ret, LDB_SUCCESS); + + /* the same elements in reverse order */ + ret = ldb_msg_find_common_values(NULL, *state, el2, el, 0); + assert_int_equal(ret, LDB_SUCCESS); + + ret = ldb_msg_find_common_values(NULL, *state, el3, el, 0); + assert_int_equal(ret, LDB_SUCCESS); + + /* 6, 7 collide */ + ret = ldb_msg_find_common_values(NULL, *state, el2, el3, 0); + assert_int_equal(ret, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS); + + /* and again */ + ret = ldb_msg_find_common_values(NULL, *state, el3, el2, 0); + assert_int_equal(ret, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS); + + /* make sure the arrays haven't changed */ + assert_element_equal(el, orig); + assert_element_equal(el2, orig2); + assert_element_equal(el3, orig3); + + /* now with the control permisive flag, the first element should be + modified to remove the overlap.*/ + + /* 6, 7 collide, so el2 will only have 4 and 5 */ + ret = ldb_msg_find_common_values(NULL, *state, el2, el3, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + + assert_element_equal(el3, orig3); + assert_int_not_equal(el2->num_values, orig2->num_values); + assert_int_equal(el2->num_values, 2); + el2b = new_msg_element(*state, "test", 4, 2); + assert_element_equal(el2, el2b); + + /* now try the same things with a long and a short value list. + this should still trigger the quadratic path. + */ + el2 = new_msg_element(*state, "test", 4, 10); + orig2 = new_msg_element(*state, "test", 4, 10); + + /* no collisions */ + ret = ldb_msg_find_common_values(NULL, *state, el, el2, 0); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, el2, el, 0); + assert_int_equal(ret, LDB_SUCCESS); + + /*collisions */ + ret = ldb_msg_find_common_values(NULL, *state, el3, el2, 0); + assert_int_equal(ret, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS); + + assert_element_equal(el, orig); + assert_element_equal(el2, orig2); + assert_element_equal(el3, orig3); + + /*collisions with permissive flag*/ + ret = ldb_msg_find_common_values(NULL, *state, el3, el2, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + assert_element_equal(el2, orig2); + assert_int_equal(el3->num_values, 0); + + /* permutations involving empty elements. + everything should succeed. */ + ret = ldb_msg_find_common_values(NULL, *state, el3, el2, 0); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, el3, el, 0); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, el2, el3, 0); + assert_int_equal(ret, LDB_SUCCESS); + assert_int_equal(el2->num_values, orig2->num_values); + ret = ldb_msg_find_common_values(NULL, *state, el3, el2, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + assert_int_equal(el2->num_values, orig2->num_values); + assert_int_equal(el3->num_values, 0); /* el3 is now empty */ + ret = ldb_msg_find_common_values(NULL, *state, el2, el3, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, el3, empty, 0); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, empty, empty, 0); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, empty, el3, 0); + assert_int_equal(ret, LDB_SUCCESS); + + assert_element_equal(el2, orig2); + assert_element_equal(el, orig); + assert_int_equal(el3->num_values, 0); + + /* now with two large value lists */ + el = new_msg_element(*state, "test", 0, 12); + orig = new_msg_element(*state, "test", 0, 12); + el4 = new_msg_element(*state, "test", 12, 12); + orig4 = new_msg_element(*state, "test", 12, 12); + + /* no collisions */ + ret = ldb_msg_find_common_values(NULL, *state, el, el4, 0); + assert_int_equal(ret, LDB_SUCCESS); + + ret = ldb_msg_find_common_values(NULL, *state, el4, el, 0); + assert_int_equal(ret, LDB_SUCCESS); + + /* collisions */ + ret = ldb_msg_find_common_values(NULL, *state, el4, el2, 0); + assert_int_equal(ret, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS); + ret = ldb_msg_find_common_values(NULL, *state, el2, el4, 0); + assert_int_equal(ret, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS); + ret = ldb_msg_find_common_values(NULL, *state, el2, el, 0); + assert_int_equal(ret, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS); + + assert_element_equal(el, orig); + assert_element_equal(el2, orig2); + assert_element_equal(el4, orig4); + + /* with permissive control, but no collisions */ + ret = ldb_msg_find_common_values(NULL, *state, el, el4, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, el4, el, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + + assert_element_equal(el, orig); + assert_element_equal(el4, orig4); + + /* now with collisions, thus modifications. + At this stage: + el is 0-11 (inclusive) + e2 is 4-13 + el3 is empty + el4 is 12-23 + */ + ret = ldb_msg_find_common_values(NULL, *state, el4, el2, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + assert_element_equal(el2, orig2); + assert_int_not_equal(el4->num_values, orig4->num_values); + /* 4 should start at 14 */ + orig4 = new_msg_element(*state, "test", 14, 10); + assert_element_equal(el4, orig4); + + ret = ldb_msg_find_common_values(NULL, *state, el2, el, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + assert_element_equal(el, orig); + assert_int_not_equal(el2->num_values, orig2->num_values); + orig2 = new_msg_element(*state, "test", 12, 2); + assert_element_equal(el2, orig2); + + /* test the empty el against the full elements */ + ret = ldb_msg_find_common_values(NULL, *state, el, empty, 0); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, empty, el, 0); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, el, empty, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + ret = ldb_msg_find_common_values(NULL, *state, empty, el, remove_dupes); + assert_int_equal(ret, LDB_SUCCESS); + assert_element_equal(el, orig); + assert_element_equal(empty, el3); + + /* make sure an identical element with a different name is rejected */ + el2 = new_msg_element(*state, "fish", 12, 2); + ret = ldb_msg_find_common_values(NULL, *state, el2, el, remove_dupes); + assert_int_equal(ret, LDB_ERR_INAPPROPRIATE_MATCHING); +} + + + +int main(int argc, const char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_ldb_msg_find_duplicate_val, + ldb_msg_setup, + ldb_msg_teardown), + cmocka_unit_test_setup_teardown( + test_ldb_msg_find_common_values, + ldb_msg_setup, + ldb_msg_teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} |