From d6ef4adf46b52c4ef359f91eaf2cf7ea53cee78a Mon Sep 17 00:00:00 2001
From: Petr Cech <pcech@redhat.com>
Date: Fri, 27 Nov 2015 06:39:37 -0500
Subject: [PATCH 2/2] TEST_TOOLS_COLONDB: Add tests for sss_colondb_*

There are three functions at API of colondb wrapper:
 * sss_colondb_open()
 * sss_colondb_readline()
 * sss_colondb_writeline()

This patch adds tests for all of them.

We test those cases:
 * open nonexisting file for read
 * open nonexisting file for write
 * open existing empty file for read
 * open existing file with records for read
 * open existing empty file for write
 * open existing file with records for write
 * write to empty file
 * write to file with existing records
 * sss_colondb_open()
 * sss_colondb_readline()
 * sss_colondb_write_line()

Resolves:
https://fedorahosted.org/sssd/ticket/2764
---
 Makefile.am                           |  16 ++
 src/tests/cmocka/test_tools_colondb.c | 414 ++++++++++++++++++++++++++++++++++
 2 files changed, 430 insertions(+)
 create mode 100644 src/tests/cmocka/test_tools_colondb.c

diff --git a/Makefile.am b/Makefile.am
index d7a6f29520980d933cf0afddf8d041b2d6f3b2ef..6763197a82a38f143436ac0be536dc29f51ad8a6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -241,6 +241,7 @@ if HAVE_CMOCKA
         pam-srv-tests \
         test_ipa_subdom_util \
         test_ipa_subdom_server \
+        test_tools_colondb \
         test_krb5_wait_queue \
         test_cert_utils \
         test_ldap_id_cleanup \
@@ -2556,6 +2557,21 @@ test_ipa_subdom_server_LDADD = \
     libdlopen_test_providers.la \
     $(NULL)
 
+test_tools_colondb_SOURCES = \
+    src/tests/cmocka/test_tools_colondb.c \
+    src/tools/common/sss_colondb.c \
+    $(NULL)
+test_tools_colondb_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(NULL)
+test_tools_colondb_LDFLAGS = \
+    $(NULL)
+test_tools_colondb_LDADD = \
+    $(CMOCKA_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS) \
+    libsss_test_common.la \
+    $(NULL)
+
 test_krb5_wait_queue_SOURCES = \
     src/tests/cmocka/common_mock_be.c \
     src/tests/cmocka/test_krb5_wait_queue.c \
diff --git a/src/tests/cmocka/test_tools_colondb.c b/src/tests/cmocka/test_tools_colondb.c
new file mode 100644
index 0000000000000000000000000000000000000000..c30085aa216d63118227fb8d8459a4a14517237d
--- /dev/null
+++ b/src/tests/cmocka/test_tools_colondb.c
@@ -0,0 +1,414 @@
+/*
+    Authors:
+        Petr Čech <pcech@redhat.com>
+
+    Copyright (C) 2015 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 <talloc.h>
+#include <errno.h>
+#include <popt.h>
+
+#include "tests/cmocka/common_mock.h"
+#include "src/tools/common/sss_colondb.h"
+
+#define TESTS_PATH "tp_" BASE_FILE_STEM
+#define TESTS_FILE "test_colondb.ldb"
+
+const char *TEST_STRING1 = "white";
+const int TEST_INT1 = 12;
+
+const char *TEST_STRING2 = "black";
+const int TEST_INT2 = 34;
+
+struct test_colondb_ctx {
+    bool is_file_created;
+};
+
+static void create_dir(const char *path)
+{
+    errno_t ret;
+    bool is_dir_created = false;
+
+    ret = mkdir(path, 0775);
+    is_dir_created = ! (ret != 0 && errno != EEXIST);
+    if (!is_dir_created) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not create test directory\n");
+    }
+    assert_true(is_dir_created);
+}
+
+static void create_empty_file(const char *path, const char *name)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    char *file_name = NULL;
+    FILE *fp = NULL;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+    }
+    assert_non_null(tmp_ctx);
+
+    create_dir(path);
+
+    file_name = talloc_asprintf(tmp_ctx, "%s/%s", path, name);
+    if (file_name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not construct colondb path\n");
+    }
+    assert_non_null(file_name);
+
+    fp = fopen(file_name, "w");
+    if (fp == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not create file %s\n", file_name);
+    }
+    assert_non_null(fp);
+    fclose(fp);
+
+    talloc_free(tmp_ctx);
+}
+
+static void create_nonempty_file(const char *path, const char *name)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    struct sss_colondb *db = NULL;
+    struct sss_colondb_write_field table[] = {
+        { SSS_COLONDB_STRING, { .str = TEST_STRING2 } },
+        { SSS_COLONDB_UINT32, { .uint32 = TEST_INT2 } },
+        { SSS_COLONDB_SENTINEL, { 0 } }
+    };
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+    }
+    assert_non_null(tmp_ctx);
+
+    create_empty_file(TESTS_PATH, TESTS_FILE);
+
+    db = sss_colondb_open(tmp_ctx, SSS_COLONDB_WRITE,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+
+    sss_colondb_writeline(db, table);
+
+    talloc_free(tmp_ctx);
+}
+
+static int setup(void **state, int file_state)
+{
+    struct test_colondb_ctx *test_ctx = NULL;
+
+    assert_true(leak_check_setup());
+
+    test_ctx = talloc_zero(global_talloc_context, struct test_colondb_ctx);
+    assert_non_null(test_ctx);
+
+    switch (file_state) {
+    case 0:
+        test_ctx->is_file_created = false;
+        break;
+    case 1:
+        create_empty_file(TESTS_PATH, TESTS_FILE);
+        test_ctx->is_file_created = true;
+        break;
+    case 2:
+        create_nonempty_file(TESTS_PATH, TESTS_FILE);
+        test_ctx->is_file_created = true;
+        break;
+    default:
+        test_ctx->is_file_created = false;
+        break;
+    }
+
+    *state = test_ctx;
+
+    return 0;
+}
+
+static int without_file_setup(void **state)
+{
+    return setup(state, 0);
+}
+
+static int with_empty_file_setup(void **state)
+{
+    return setup(state, 1);
+}
+
+static int with_nonempty_file_setup(void **state)
+{
+    return setup(state, 2);
+}
+
+static int teardown(void **state)
+{
+    struct test_colondb_ctx *test_ctx = NULL;
+    bool is_config_deleted = false;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    if (test_ctx->is_file_created) {
+        errno = 0;
+        ret = unlink(TESTS_PATH "/" TESTS_FILE);
+        is_config_deleted = ! (ret != 0 && errno != ENOENT);
+        if (!is_config_deleted) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE, "Could not delete the test config "
+                                       "ldb file [%d]: (%s)\n",
+                  ret, sss_strerror(ret));
+        }
+        assert_true(is_config_deleted);
+    }
+
+    talloc_zfree(*state);
+    test_dom_suite_cleanup(TESTS_PATH, NULL, NULL);
+    assert_true(leak_check_teardown());
+
+    return 0;
+}
+
+void test_open_nonexist_for_read(void **state)
+{
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    check_leaks_push(test_ctx);
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_READ,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_true(check_leaks_pop(test_ctx));
+    assert_null(db);
+}
+
+void test_open_nonexist_for_write(void **state)
+{
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    check_leaks_push(test_ctx);
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE,
+                          TESTS_PATH "/" TESTS_FILE);
+    test_ctx->is_file_created = true;
+    assert_true(check_leaks_pop(test_ctx));
+    assert_null(db);
+}
+
+void test_open_exist_for_read(void **state)
+{
+
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_READ,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+}
+
+void test_open_exist_for_write(void **state)
+{
+
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+}
+
+void test_open_nonempty_for_read(void **state)
+{
+
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_READ,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+}
+
+void test_open_nonempty_for_write(void **state)
+{
+
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+}
+
+void test_write_to_empty(void **state)
+{
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+    struct sss_colondb_write_field table[] = {
+        { SSS_COLONDB_STRING, { .str = TEST_STRING1 } },
+        { SSS_COLONDB_UINT32, { .uint32 = TEST_INT1 } },
+        { SSS_COLONDB_SENTINEL, { 0 } }
+    };
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+
+    ret = sss_colondb_writeline(db, table);
+    assert_int_equal(ret, 0);
+}
+
+void test_write_to_nonempty(void **state)
+{
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+    struct sss_colondb_write_field table[] = {
+        { SSS_COLONDB_STRING, { .str = TEST_STRING1 } },
+        { SSS_COLONDB_UINT32, { .uint32 = TEST_INT1 } },
+        { SSS_COLONDB_SENTINEL, { 0 } }
+    };
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+
+    ret = sss_colondb_writeline(db, table);
+    assert_int_equal(ret, 0);
+}
+
+void test_read_from_nonempty(void **state)
+{
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+    errno_t ret;
+    const char *string;
+    uint32_t number;
+    struct sss_colondb_read_field table[] = {
+        { SSS_COLONDB_STRING, { .str = &string } },
+        { SSS_COLONDB_UINT32, { .uint32 = &number } },
+        { SSS_COLONDB_SENTINEL, { 0 } }
+    };
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_READ,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+
+    ret = sss_colondb_readline(test_ctx, db, table);
+    assert_int_equal(ret, 0);
+    assert_string_equal(string, TEST_STRING2);
+    assert_int_equal(number, TEST_INT2);
+}
+
+void test_read_from_empty(void **state)
+{
+    struct test_colondb_ctx *test_ctx = NULL;
+    struct sss_colondb *db = NULL;
+    errno_t ret;
+    const char *string;
+    uint32_t number;
+    struct sss_colondb_read_field table[] = {
+        { SSS_COLONDB_STRING, { .str = &string } },
+        { SSS_COLONDB_UINT32, { .uint32 = &number } },
+        { SSS_COLONDB_SENTINEL, { 0 } }
+    };
+
+    test_ctx = talloc_get_type_abort(*state, struct test_colondb_ctx);
+
+    db = sss_colondb_open(test_ctx, SSS_COLONDB_READ,
+                          TESTS_PATH "/" TESTS_FILE);
+    assert_non_null(db);
+
+    ret = sss_colondb_readline(test_ctx, db, table);
+    assert_int_equal(ret, -1);
+}
+
+int main(int argc, const char *argv[])
+{
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        POPT_TABLEEND
+    };
+
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_open_nonexist_for_read,
+                                        without_file_setup, teardown),
+        cmocka_unit_test_setup_teardown(test_open_nonexist_for_write,
+                                        without_file_setup, teardown),
+        cmocka_unit_test_setup_teardown(test_open_exist_for_read,
+                                        with_empty_file_setup, teardown),
+        cmocka_unit_test_setup_teardown(test_open_exist_for_read,
+                                        with_empty_file_setup, teardown),
+        cmocka_unit_test_setup_teardown(test_open_nonempty_for_read,
+                                        with_nonempty_file_setup, teardown),
+        cmocka_unit_test_setup_teardown(test_open_nonempty_for_write,
+                                        with_nonempty_file_setup, teardown),
+
+        cmocka_unit_test_setup_teardown(test_write_to_empty,
+                                        with_empty_file_setup, teardown),
+        cmocka_unit_test_setup_teardown(test_write_to_nonempty,
+                                        with_nonempty_file_setup, teardown),
+
+        cmocka_unit_test_setup_teardown(test_read_from_empty,
+                                        with_empty_file_setup, teardown),
+        cmocka_unit_test_setup_teardown(test_read_from_nonempty,
+                                        with_nonempty_file_setup, teardown),
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while ((opt = poptGetNextOpt(pc)) != -1) {
+        switch (opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0),
+                    poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_CLI_INIT(debug_level);
+
+    /* Even though normally the tests should clean up after themselves
+     * they might not after a failed run. Remove the old db to be sure */
+    tests_set_cwd();
+    test_dom_suite_cleanup(TESTS_PATH, NULL, NULL);
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
-- 
2.4.3

