From 88b23bf50dd1c12413f3314639de2c3909bd9098 Mon Sep 17 00:00:00 2001
From: Alexey Tikhonov <atikhono@redhat.com>
Date: Tue, 28 Jan 2020 19:26:08 +0100
Subject: [PATCH 23/23] TESTS: added sss_ptr_hash unit test
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
---
Makefile.am | 1 +
src/tests/cmocka/test_sss_ptr_hash.c | 193 +++++++++++++++++++++++++++
src/tests/cmocka/test_utils.c | 9 ++
src/tests/cmocka/test_utils.h | 6 +
4 files changed, 209 insertions(+)
create mode 100644 src/tests/cmocka/test_sss_ptr_hash.c
diff --git a/Makefile.am b/Makefile.am
index 57ba51356..c991f2aa0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3054,6 +3054,7 @@ test_ipa_idmap_LDADD = \
test_utils_SOURCES = \
src/tests/cmocka/test_utils.c \
src/tests/cmocka/test_string_utils.c \
+ src/tests/cmocka/test_sss_ptr_hash.c \
src/p11_child/p11_child_common_utils.c \
$(NULL)
if BUILD_SSH
diff --git a/src/tests/cmocka/test_sss_ptr_hash.c b/src/tests/cmocka/test_sss_ptr_hash.c
new file mode 100644
index 000000000..1458238f5
--- /dev/null
+++ b/src/tests/cmocka/test_sss_ptr_hash.c
@@ -0,0 +1,193 @@
+/*
+ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "tests/cmocka/common_mock.h"
+#include "util/sss_ptr_hash.h"
+
+static const int MAX_ENTRIES_AMOUNT = 5;
+
+static void populate_table(hash_table_t *table, int **payloads)
+{
+ char key[2] = {'z', 0};
+
+ for (int i = 0; i < MAX_ENTRIES_AMOUNT; ++i) {
+ payloads[i] = talloc_zero(global_talloc_context, int);
+ assert_non_null(payloads[i]);
+ *payloads[i] = i;
+ key[0] = '0'+(char)i;
+ assert_int_equal(sss_ptr_hash_add(table, key, payloads[i], int), 0);
+ }
+
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT);
+}
+
+static void free_payload_cb(hash_entry_t *item, hash_destroy_enum type, void *pvt)
+{
+ int *counter;
+
+ assert_non_null(item);
+ assert_non_null(item->value.ptr);
+ talloc_zfree(item->value.ptr);
+
+ assert_non_null(pvt);
+ counter = (int *)pvt;
+ (*counter)++;
+}
+
+void test_sss_ptr_hash_with_free_cb(void **state)
+{
+ hash_table_t *table;
+ int free_counter = 0;
+ int *payloads[MAX_ENTRIES_AMOUNT];
+
+ table = sss_ptr_hash_create(global_talloc_context,
+ free_payload_cb,
+ &free_counter);
+ assert_non_null(table);
+
+ populate_table(table, payloads);
+
+ /* check explicit removal from the hash */
+ sss_ptr_hash_delete(table, "1", false);
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-1);
+ assert_int_equal(free_counter, 1);
+
+ /* check implicit removal triggered by payload deletion */
+ talloc_free(payloads[3]);
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-2);
+ assert_int_equal(free_counter, 2);
+
+ /* try to remove non existent entry */
+ sss_ptr_hash_delete(table, "q", false);
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-2);
+ assert_int_equal(free_counter, 2);
+
+ /* clear all */
+ sss_ptr_hash_delete_all(table, false);
+ assert_int_equal((int)hash_count(table), 0);
+ assert_int_equal(free_counter, MAX_ENTRIES_AMOUNT);
+
+ /* check that table is still operable */
+ populate_table(table, payloads);
+ sss_ptr_hash_delete(table, "2", false);
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-1);
+ assert_int_equal(free_counter, MAX_ENTRIES_AMOUNT+1);
+
+ talloc_free(table);
+ assert_int_equal(free_counter, MAX_ENTRIES_AMOUNT*2);
+}
+
+struct table_wrapper
+{
+ hash_table_t **table;
+};
+
+static void lookup_cb(hash_entry_t *item, hash_destroy_enum type, void *pvt)
+{
+ hash_table_t *table;
+ hash_key_t *keys;
+ unsigned long count;
+ int *value = NULL;
+ int sum = 0;
+
+ assert_non_null(pvt);
+ table = *((struct table_wrapper *)pvt)->table;
+ assert_non_null(table);
+
+ if (type == HASH_TABLE_DESTROY) {
+ /* table is being destroyed */
+ return;
+ }
+
+ assert_int_equal(hash_keys(table, &count, &keys), HASH_SUCCESS);
+ for (unsigned int i = 0; i < count; ++i) {
+ assert_int_equal(keys[i].type, HASH_KEY_STRING);
+ value = sss_ptr_hash_lookup(table, keys[i].c_str, int);
+ assert_non_null(value);
+ sum += *value;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "sum of all values = %d\n", sum);
+ talloc_free(keys);
+}
+
+/* main difference with `test_sss_ptr_hash_with_free_cb()`
+ * is that table cb here doesn't delete payload so
+ * this is requested via `free_value(s)` arg
+ */
+void test_sss_ptr_hash_with_lookup_cb(void **state)
+{
+ hash_table_t *table;
+ struct table_wrapper wrapper;
+ int *payloads[MAX_ENTRIES_AMOUNT];
+
+ wrapper.table = &table;
+ table = sss_ptr_hash_create(global_talloc_context,
+ lookup_cb,
+ &wrapper);
+ assert_non_null(table);
+
+ populate_table(table, payloads);
+
+ /* check explicit removal from the hash */
+ sss_ptr_hash_delete(table, "2", true);
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-1);
+
+ /* check implicit removal triggered by payload deletion */
+ talloc_free(payloads[0]);
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-2);
+
+ /* clear all */
+ sss_ptr_hash_delete_all(table, true);
+ assert_int_equal((int)hash_count(table), 0);
+ /* teardown function shall verify there are no leaks
+ * on global_talloc_context and so that payloads[] were freed
+ */
+
+ /* check that table is still operable */
+ populate_table(table, payloads);
+
+ talloc_free(table);
+ /* d-tor triggers hash_destroy() but since cb here doesn free payload
+ * this should be done manually
+ */
+ for (int i = 0; i < MAX_ENTRIES_AMOUNT; ++i) {
+ talloc_free(payloads[i]);
+ }
+}
+
+/* Just smoke test to verify that absence of cb doesn't break anything */
+void test_sss_ptr_hash_without_cb(void **state)
+{
+ hash_table_t *table;
+ int *payloads[MAX_ENTRIES_AMOUNT];
+
+ table = sss_ptr_hash_create(global_talloc_context, NULL, NULL);
+ assert_non_null(table);
+
+ populate_table(table, payloads);
+
+ sss_ptr_hash_delete(table, "4", true);
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-1);
+
+ talloc_free(payloads[1]);
+ assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-2);
+
+ sss_ptr_hash_delete_all(table, true);
+ assert_int_equal((int)hash_count(table), 0);
+
+ talloc_free(table);
+}
diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index 666f32903..c5eda4dd2 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -2055,6 +2055,15 @@ int main(int argc, const char *argv[])
cmocka_unit_test_setup_teardown(test_sss_get_domain_mappings_content,
setup_dom_list_with_subdomains,
teardown_dom_list),
+ cmocka_unit_test_setup_teardown(test_sss_ptr_hash_with_free_cb,
+ setup_leak_tests,
+ teardown_leak_tests),
+ cmocka_unit_test_setup_teardown(test_sss_ptr_hash_with_lookup_cb,
+ setup_leak_tests,
+ teardown_leak_tests),
+ cmocka_unit_test_setup_teardown(test_sss_ptr_hash_without_cb,
+ setup_leak_tests,
+ teardown_leak_tests),
};
/* Set debug level to invalid value so we can decide if -d 0 was used. */
diff --git a/src/tests/cmocka/test_utils.h b/src/tests/cmocka/test_utils.h
index e93e0da25..44b9479f9 100644
--- a/src/tests/cmocka/test_utils.h
+++ b/src/tests/cmocka/test_utils.h
@@ -33,4 +33,10 @@ void test_guid_blob_to_string_buf(void **state);
void test_get_last_x_chars(void **state);
void test_concatenate_string_array(void **state);
+/* from src/tests/cmocka/test_sss_ptr_hash.c */
+void test_sss_ptr_hash_with_free_cb(void **state);
+void test_sss_ptr_hash_with_lookup_cb(void **state);
+void test_sss_ptr_hash_without_cb(void **state);
+
+
#endif /* __TESTS__CMOCKA__TEST_UTILS_H__ */
--
2.20.1