[SSSD] [PATCH] RFC: Unit test for the NSS responder based on the cmocka library

Jakub Hrozek jhrozek at redhat.com
Fri Mar 8 16:25:53 UTC 2013


On Fri, Mar 08, 2013 at 02:46:10PM +0100, Jakub Hrozek wrote:
> On Wed, Mar 06, 2013 at 09:33:12PM +0100, Sumit Bose wrote:
> > On Tue, Mar 05, 2013 at 05:25:45PM +0100, Jakub Hrozek wrote:
> > > On Fri, Mar 01, 2013 at 02:38:08PM +0100, Pavel Březina wrote:
> > > 
> > > > 
> > > > Create a macro for:
> > > > *body = (uint8_t *) mock() => mock_as(uint8_t*)
> > > 
> > > OK, done.
> > > 
> > > New tests attached.
> > 
> > Maybe you can consider the attached patch for inclusion as well? It will
> > silence a "cast to pointer from integer of different size
> > [-Wint-to-pointer-cast]" compiler warning.
> > 
> > What do you think of asking upstream if they would like to include
> > 
> > #define mock_type(type) ((type) mock())
> > 
> > and maybe
> > 
> > #define mock_ptr_type(type) ((type) (uintptr_t) mock())
> > 
> > or similar? To avoid conflicts I would recommend to add a prefix, e.g.
> > sss_mock_type and sss_mock_ptr_type.
> 
> Thank you, I've merged your patch and used prefix for the macros. I will
> ask cmocka upstream to provide their macros so when/if they are
> available we can simply use those.
> 
> New patches are attached.

Pavel asked me off-list to use an enum for deciding whether we are
calling a real function or a wrapper. New patches are attached.
-------------- next part --------------
>From 55498adbd4ec538945d5f5318b47827165f949a7 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 12 Feb 2013 14:47:35 +0100
Subject: [PATCH 1/4] Detect the presence of libcmocka during configure

---
 configure.ac              |  2 ++
 src/external/libcmocka.m4 | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+)
 create mode 100644 src/external/libcmocka.m4

diff --git a/configure.ac b/configure.ac
index af976151ad3c90bfcb056dd7d8d24bbc8b884ca5..0e7712389d99cf1b903ae1dbf063d889be62d7b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -137,6 +137,7 @@ m4_include([src/external/ldap.m4])
 m4_include([src/external/libpcre.m4])
 m4_include([src/external/krb5.m4])
 m4_include([src/external/libcares.m4])
+m4_include([src/external/libcmocka.m4])
 m4_include([src/external/docbook.m4])
 m4_include([src/external/sizes.m4])
 m4_include([src/external/python.m4])
@@ -269,6 +270,7 @@ AC_PATH_PROG([DOXYGEN], [doxygen], [false])
 AM_CONDITIONAL([HAVE_DOXYGEN], [test x$DOXYGEN != xfalse ])
 
 AM_CONDITIONAL([HAVE_CHECK], [test x$have_check != x])
+AM_CONDITIONAL([HAVE_CMOCKA], [test x$have_cmocka != x])
 
 abs_build_dir=`pwd`
 AC_DEFINE_UNQUOTED([ABS_BUILD_DIR], ["$abs_build_dir"], [Absolute path to the build directory])
diff --git a/src/external/libcmocka.m4 b/src/external/libcmocka.m4
new file mode 100644
index 0000000000000000000000000000000000000000..c57327612e2eec2411f18b640c4e2eaed2b31bc1
--- /dev/null
+++ b/src/external/libcmocka.m4
@@ -0,0 +1,19 @@
+dnl this file will be simplified when cmocka carries a .pc file
+AC_SUBST(CMOCKA_LIBS)
+AC_SUBST(CMOCKA_CFLAGS)
+
+AC_CHECK_HEADERS(
+    [setjmp.h cmocka.h],
+    [AC_CHECK_LIB([cmocka], [_will_return],
+                  [ CMOCKA_LIBS="-lcmocka"
+                    have_cmocka="yes" ],
+                  [AC_MSG_WARN([No libcmocka library found])
+                    have_cmocka="no" ])],
+    [AC_MSG_WARN([libcmocka header files not installed])],
+    [[ #include <stdarg.h>
+     # include <stddef.h>
+     #ifdef HAVE_SETJMP_H
+     # include <setjmp.h>
+     #endif
+    ]]
+)
-- 
1.8.1.4

-------------- next part --------------
>From 3b43b21110ce110ed0c12063fa265f3ef13c7f91 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 12 Feb 2013 13:18:00 +0100
Subject: [PATCH 2/4] Add utility functions for tests that use sysdb or tevent.

There was shared code for several unit tests that connected to their own
sysdb instance. This patch adds common code to run a generic request to
completion or connect to a test sysdb.
---
 src/tests/common.h     |  40 ++++++++++++
 src/tests/common_dom.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/tests/common_tev.c |  60 ++++++++++++++++++
 3 files changed, 267 insertions(+)
 create mode 100644 src/tests/common_dom.c
 create mode 100644 src/tests/common_tev.c

diff --git a/src/tests/common.h b/src/tests/common.h
index f13181b751fef517f609c6208977cc82a825014e..b7eee46eff303df475909376c663a23fcc02a1b5 100644
--- a/src/tests/common.h
+++ b/src/tests/common.h
@@ -55,4 +55,44 @@ errno_t
 compare_sdap_attr_maps(struct sdap_attr_map *map1, size_t size1,
                        struct sdap_attr_map *map2);
 
+/* A common test structure for tests that require a domain to be set up. */
+struct sss_test_ctx {
+    struct sysdb_ctx *sysdb;
+    struct confdb_ctx *confdb;
+    struct tevent_context *ev;
+    struct sss_domain_info *dom;
+
+    bool done;
+    int error;
+};
+
+struct sss_test_conf_param {
+    const char *key;
+    const char *value;
+};
+
+struct sss_test_ctx *
+create_dom_test_ctx(TALLOC_CTX *mem_ctx,
+                    const char *tests_path,
+                    const char *confdb_path,
+                    const char *sysdb_path,
+                    const char *domain_name,
+                    const char *id_provider,
+                    struct sss_test_conf_param *params);
+
+void test_dom_suite_setup(const char *tests_path);
+
+void test_dom_suite_cleanup(const char *tests_path,
+                            const char *confdb_path,
+                            const char *sysdb_path);
+
+struct tevent_req *
+test_request_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, errno_t err);
+
+#define test_req_succeed_send(mem_ctx, ev) test_request_send(mem_ctx, ev, 0)
+
+errno_t test_request_recv(struct tevent_req *req);
+
+int test_ev_loop(struct sss_test_ctx *tctx);
+
 #endif /* !__TESTS_COMMON_H__ */
diff --git a/src/tests/common_dom.c b/src/tests/common_dom.c
new file mode 100644
index 0000000000000000000000000000000000000000..f835621004d19259b9d9411da6c1d64893a82ebb
--- /dev/null
+++ b/src/tests/common_dom.c
@@ -0,0 +1,167 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Common utilities for tests that exercise domains
+
+    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 "tests/common.h"
+
+struct sss_test_ctx *
+create_dom_test_ctx(TALLOC_CTX *mem_ctx,
+                    const char *tests_path,
+                    const char *confdb_path,
+                    const char *sysdb_path,
+                    const char *domain_name,
+                    const char *id_provider,
+                    struct sss_test_conf_param *params)
+{
+    struct sss_test_ctx *test_ctx;
+    char *conf_db;
+    size_t i;
+    const char *val[2];
+    val[1] = NULL;
+    errno_t ret;
+    char *dompath;
+
+    test_ctx = talloc_zero(mem_ctx, struct sss_test_ctx);
+    if (test_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed\n"));
+        goto fail;
+    }
+
+    /* Create an event context */
+    test_ctx->ev = tevent_context_init(test_ctx);
+    if (test_ctx->ev == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_context_init failed\n"));
+        goto fail;
+    }
+
+    conf_db = talloc_asprintf(test_ctx, "%s/%s", tests_path, confdb_path);
+    if (conf_db == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf failed\n"));
+        goto fail;
+    }
+
+    /* Connect to the conf db */
+    ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("confdb_init failed: %d\n", ret));
+        goto fail;
+    }
+
+    val[0] = domain_name;
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           "config/sssd", "domains", val);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("cannot add domain: %d\n", ret));
+        goto fail;
+    }
+
+    dompath = talloc_asprintf(test_ctx, "config/domain/%s", domain_name);
+    if (dompath == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf failed\n"));
+        goto fail;
+    }
+
+    val[0] = id_provider;
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           dompath, "id_provider", val);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("cannot add id_provider: %d\n", ret));
+        goto fail;
+    }
+
+    if (params) {
+        for (i=0; params[i].key; i++) {
+            val[0] = params[i].key;
+            ret = confdb_add_param(test_ctx->confdb, true,
+                                   dompath, params[i].value, val);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      ("cannot add parameter %s: %d\n", params[i].key, ret));
+                goto fail;
+            }
+        }
+    }
+
+    ret = sssd_domain_init(test_ctx, test_ctx->confdb, domain_name,
+                           tests_path, &test_ctx->dom);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("cannot add id_provider: %d\n", ret));
+        goto fail;
+    }
+    test_ctx->sysdb = test_ctx->dom->sysdb;
+
+    return test_ctx;
+
+fail:
+    talloc_free(test_ctx);
+    return NULL;
+}
+
+void test_dom_suite_setup(const char *tests_path)
+{
+    errno_t ret;
+
+    /* Create tests directory if it doesn't exist */
+    /* (relative to current dir) */
+    ret = mkdir(tests_path, 0775);
+    if (ret != 0 && errno != EEXIST) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Could not create test directory\n"));
+    }
+}
+
+void test_dom_suite_cleanup(const char *tests_path,
+                            const char *confdb_path,
+                            const char *sysdb_path)
+{
+    errno_t ret;
+    char *conf_db;
+    char *sys_db;
+
+    conf_db = talloc_asprintf(NULL, "%s/%s", tests_path, confdb_path);
+    sys_db = talloc_asprintf(NULL, "%s/%s", tests_path, sysdb_path);
+    if (!conf_db || !sys_db) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Could not construct db paths\n"));
+    }
+
+    errno = 0;
+    ret = unlink(conf_db);
+    if (ret != 0 && errno != ENOENT) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Could not delete the test config ldb file (%d) (%s)\n",
+               errno, strerror(errno)));
+    }
+
+    errno = 0;
+    ret = unlink(sys_db);
+    if (ret != 0 && errno != ENOENT) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Could not delete the test ldb file (%d) (%s)\n",
+               errno, strerror(errno)));
+    }
+
+    talloc_free(conf_db);
+    talloc_free(sys_db);
+}
diff --git a/src/tests/common_tev.c b/src/tests/common_tev.c
new file mode 100644
index 0000000000000000000000000000000000000000..83ba9fd751616402529d5e03f84e50128365c6da
--- /dev/null
+++ b/src/tests/common_tev.c
@@ -0,0 +1,60 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Common utilities for tests that exercise domains
+
+    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 <tevent.h>
+#include <talloc.h>
+#include <errno.h>
+
+#include "tests/common.h"
+
+struct tevent_req *
+test_request_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, errno_t err)
+{
+    struct tevent_req *req;
+    int state;
+
+    req = tevent_req_create(mem_ctx, &state, int);
+    if (!req) return NULL;
+
+    if (err == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, err);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+errno_t test_request_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+int test_ev_loop(struct sss_test_ctx *tctx)
+{
+    while (!tctx->done)
+        tevent_loop_once(tctx->ev);
+
+    return tctx->error;
+}
-- 
1.8.1.4

-------------- next part --------------
>From c693bb1d171ab0022f7ce61b614c9f6a2a549c8b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 12 Feb 2013 13:58:46 +0100
Subject: [PATCH 3/4] Move sss_cmd_execute from client to responder code.

I think it logically belongs there and allows to better exercise the
responder commands from unit tests.
---
 src/responder/common/responder.h        |  4 +++-
 src/responder/common/responder_cmd.c    |  8 +++-----
 src/responder/common/responder_common.c | 10 +++++++++-
 3 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index a265d61d920d6f649f99aed0d30c17866c3fa4a4..110bd38d317dbacb10b636a6b56cdae8b7a1af9b 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -176,9 +176,11 @@ responder_get_domain(TALLOC_CTX *sd_mem_ctx, struct resp_ctx *rctx,
 int sss_cmd_empty_packet(struct sss_packet *packet);
 int sss_cmd_send_empty(struct cli_ctx *cctx, TALLOC_CTX *freectx);
 int sss_cmd_send_error(struct cli_ctx *cctx, int err);
-int sss_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds);
 void sss_cmd_done(struct cli_ctx *cctx, void *freectx);
 int sss_cmd_get_version(struct cli_ctx *cctx);
+int sss_cmd_execute(struct cli_ctx *cctx,
+                    enum sss_cli_command cmd,
+                    struct sss_cmd_table *sss_cmds);
 struct cli_protocol_version *register_cli_protocol_version(void);
 
 struct setent_req_list;
diff --git a/src/responder/common/responder_cmd.c b/src/responder/common/responder_cmd.c
index cb57cba1a446cdf6ce1782b8d772b70c16ef1e07..3a3fca9b00682c23490509ebdd533bc01e9bb005 100644
--- a/src/responder/common/responder_cmd.c
+++ b/src/responder/common/responder_cmd.c
@@ -141,13 +141,12 @@ int sss_cmd_get_version(struct cli_ctx *cctx)
     return EOK;
 }
 
-int sss_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds)
+int sss_cmd_execute(struct cli_ctx *cctx,
+                    enum sss_cli_command cmd,
+                    struct sss_cmd_table *sss_cmds)
 {
-    enum sss_cli_command cmd;
     int i;
 
-    cmd = sss_packet_get_cmd(cctx->creq->in);
-
     for (i = 0; sss_cmds[i].cmd != SSS_CLI_NULL; i++) {
         if (cmd == sss_cmds[i].cmd) {
             return sss_cmds[i].fn(cctx);
@@ -156,7 +155,6 @@ int sss_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds)
 
     return EINVAL;
 }
-
 struct setent_req_list {
     struct setent_req_list *prev;
     struct setent_req_list *next;
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index 824c649a8c71ffad83e42e019292ea727ae2e4bf..a4355ae6c7f9831d797a06d2d0c947b0402ee81d 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -262,6 +262,14 @@ static void client_send(struct cli_ctx *cctx)
     return;
 }
 
+static int client_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds)
+{
+    enum sss_cli_command cmd;
+
+    cmd = sss_packet_get_cmd(cctx->creq->in);
+    return sss_cmd_execute(cctx, cmd, sss_cmds);
+}
+
 static void client_recv(struct cli_ctx *cctx)
 {
     int ret;
@@ -291,7 +299,7 @@ static void client_recv(struct cli_ctx *cctx)
         /* do not read anymore */
         TEVENT_FD_NOT_READABLE(cctx->cfde);
         /* execute command */
-        ret = sss_cmd_execute(cctx, cctx->rctx->sss_cmds);
+        ret = client_cmd_execute(cctx, cctx->rctx->sss_cmds);
         if (ret != EOK) {
             DEBUG(0, ("Failed to execute request, aborting client!\n"));
             talloc_free(cctx);
-- 
1.8.1.4

-------------- next part --------------
>From 6533f5beb192043c95ae9832d209a4ad0ede480d Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 12 Feb 2013 17:40:01 +0100
Subject: [PATCH 4/4] CMocka based test for the NSS responder

---
 Makefile.am                         |  43 +++
 src/tests/cmocka/common_mock.h      |  55 ++++
 src/tests/cmocka/common_mock_resp.c | 139 ++++++++++
 src/tests/cmocka/common_mock_resp.h |  62 +++++
 src/tests/cmocka/test_nss_srv.c     | 512 ++++++++++++++++++++++++++++++++++++
 5 files changed, 811 insertions(+)
 create mode 100644 src/tests/cmocka/common_mock.h
 create mode 100644 src/tests/cmocka/common_mock_resp.c
 create mode 100644 src/tests/cmocka/common_mock_resp.h
 create mode 100644 src/tests/cmocka/test_nss_srv.c

diff --git a/Makefile.am b/Makefile.am
index c66e25525c2a652681eb8ff2cd607d368d59bc81..2401c9122edd463335ee99ac32edb316ac705f70 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -138,9 +138,15 @@ endif
 
 endif
 
+if HAVE_CMOCKA
+    non_interactive_cmocka_based_tests = \
+	nss-srv-tests
+endif
+
 check_PROGRAMS = \
     stress-tests \
     krb5-child-test \
+    $(non_interactive_cmocka_based_tests) \
     $(non_interactive_check_based_tests)
 
 PYTHON_TESTS =
@@ -153,6 +159,7 @@ endif
 
 TESTS = \
     $(PYTHON_TESTS) \
+    $(non_interactive_cmocka_based_tests) \
     $(non_interactive_check_based_tests)
 
 sssdlib_LTLIBRARIES = \
@@ -1153,6 +1160,42 @@ krb5_child_test_LDADD = \
     libsss_util.la \
     libsss_test_common.la
 
+if HAVE_CMOCKA
+TEST_MOCK_OBJ = \
+     src/tests/common.c
+
+TEST_MOCK_RESP_OBJ = \
+     $(TEST_MOCK_OBJ) \
+     src/tests/common_tev.c \
+     src/tests/common_dom.c \
+     src/tests/cmocka/common_mock_resp.c \
+     src/responder/common/responder_packet.c \
+     src/responder/common/responder_cmd.c \
+     src/responder/common/negcache.c \
+     src/responder/common/responder_common.c
+
+nss_srv_tests_DEPENDENCIES = \
+     $(ldblib_LTLIBRARIES)
+nss_srv_tests_SOURCES = \
+     $(TEST_MOCK_RESP_OBJ) \
+     src/tests/cmocka/test_nss_srv.c \
+     src/responder/nss/nsssrv_cmd.c \
+     src/responder/nss/nsssrv_netgroup.c \
+     src/responder/nss/nsssrv_services.c \
+     src/responder/nss/nsssrv_mmap_cache.c
+nss_srv_tests_CFLAGS = \
+    $(AM_CFLAGS)
+nss_srv_tests_LDFLAGS = \
+    -Wl,-wrap,sss_ncache_check_user \
+    -Wl,-wrap,sss_packet_get_body \
+    -Wl,-wrap,sss_packet_get_cmd \
+    -Wl,-wrap,sss_cmd_send_empty \
+    -Wl,-wrap,sss_cmd_done
+nss_srv_tests_LDADD = \
+    $(CMOCKA_LIBS) \
+    libsss_util.la
+endif
+
 noinst_PROGRAMS = pam_test_client
 if BUILD_SUDO
 noinst_PROGRAMS += sss_sudo_cli
diff --git a/src/tests/cmocka/common_mock.h b/src/tests/cmocka/common_mock.h
new file mode 100644
index 0000000000000000000000000000000000000000..40d776411f32b8d8d2d022da2f3e9bb6c9e0ce99
--- /dev/null
+++ b/src/tests/cmocka/common_mock.h
@@ -0,0 +1,55 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Common utilities for tests that exercise domains
+
+    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/>.
+*/
+
+#ifndef __COMMON_MOCK_H_
+#define __COMMON_MOCK_H_
+
+/*
+ * 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 <setjmp.h>
+#include <cmocka.h>
+
+#include "tests/common.h"
+
+#define sss_mock_type(type) ((type) mock())
+#define sss_mock_ptr_type(type) ((type) (uintptr_t) mock())
+
+enum sss_test_wrapper_call {
+    WRAP_CALL_WRAPPER,
+    WRAP_CALL_REAL
+};
+
+#endif /* __COMMON_MOCK_H_ */
+
diff --git a/src/tests/cmocka/common_mock_resp.c b/src/tests/cmocka/common_mock_resp.c
new file mode 100644
index 0000000000000000000000000000000000000000..d74f6ffa7a76d2f80fc7cbcf46ecf9ac5bcc9878
--- /dev/null
+++ b/src/tests/cmocka/common_mock_resp.c
@@ -0,0 +1,139 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Common utilities for tests that exercise domains
+
+    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 "util/util.h"
+#include "tests/cmocka/common_mock_resp.h"
+
+/* Mock a responder context */
+struct resp_ctx *
+mock_rctx(TALLOC_CTX *mem_ctx,
+          struct tevent_context *ev,
+          struct sss_domain_info *domains,
+          void *pvt_ctx)
+{
+    struct resp_ctx *rctx;
+    errno_t ret;
+
+    rctx = talloc_zero(mem_ctx, struct resp_ctx);
+    if (!rctx) return NULL;
+
+    ret = sss_hash_create(rctx, 30, &rctx->dp_request_table);
+    if (ret != EOK) {
+        talloc_free(rctx);
+        return NULL;
+    }
+
+    rctx->ev = ev;
+    rctx->domains = domains;
+    rctx->pvt_ctx = pvt_ctx;
+    return rctx;
+}
+
+/* Mock a client context */
+struct cli_ctx *
+mock_cctx(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx)
+{
+    struct cli_ctx *cctx;
+
+    cctx = talloc_zero(mem_ctx, struct cli_ctx);
+    if (!cctx) return NULL;
+
+    cctx->creq = talloc_zero(cctx, struct cli_request);
+    if (cctx->creq == NULL) {
+        talloc_free(cctx);
+        return NULL;
+    }
+
+    cctx->rctx = rctx;
+    return cctx;
+}
+
+/* Mock DP requests that finish immediatelly and return
+ * mocked values as per previous set by mock_account_recv
+ */
+struct tevent_req *
+sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
+                        struct resp_ctx *rctx,
+                        struct sss_domain_info *dom,
+                        bool fast_reply,
+                        enum sss_dp_acct_type type,
+                        const char *opt_name,
+                        uint32_t opt_id,
+                        const char *extra)
+{
+    return test_req_succeed_send(mem_ctx, rctx->ev);
+}
+
+
+errno_t
+sss_dp_get_account_recv(TALLOC_CTX *mem_ctx,
+                        struct tevent_req *req,
+                        dbus_uint16_t *dp_err,
+                        dbus_uint32_t *dp_ret,
+                        char **err_msg)
+{
+    acct_cb_t cb;
+
+    *dp_err = sss_mock_type(dbus_uint16_t);
+    *dp_ret = sss_mock_type(dbus_uint32_t);
+    *dp_ret = sss_mock_type(dbus_uint32_t);
+
+    cb = sss_mock_ptr_type(acct_cb_t);
+    if (cb) {
+        (cb)(sss_mock_ptr_type(void *));
+    }
+
+    return test_request_recv(req);
+}
+
+void mock_account_recv(uint16_t dp_err, uint32_t dp_ret, char *msg,
+                       acct_cb_t acct_cb, void *pvt)
+{
+    will_return(sss_dp_get_account_recv, dp_err);
+    will_return(sss_dp_get_account_recv, dp_ret);
+    will_return(sss_dp_get_account_recv, msg);
+
+    will_return(sss_dp_get_account_recv, acct_cb);
+    if (acct_cb) {
+        will_return(sss_dp_get_account_recv, pvt);
+    }
+}
+
+void mock_account_recv_simple(void)
+{
+    return mock_account_recv(0, 0, NULL, NULL, NULL);
+}
+
+/* Mock subdomain requests */
+struct tevent_req *
+sss_dp_get_domains_send(TALLOC_CTX *mem_ctx,
+                        struct resp_ctx *rctx,
+                        bool force,
+                        const char *hint)
+{
+    return test_req_succeed_send(mem_ctx, rctx->ev);
+}
+
+errno_t sss_dp_get_domains_recv(struct tevent_req *req)
+{
+    return test_request_recv(req);
+}
diff --git a/src/tests/cmocka/common_mock_resp.h b/src/tests/cmocka/common_mock_resp.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ddcc236e09c12034a6079b74adeb099d38d22df
--- /dev/null
+++ b/src/tests/cmocka/common_mock_resp.h
@@ -0,0 +1,62 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Common utilities for tests that exercise domains
+
+    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/>.
+*/
+
+#ifndef __COMMON_MOCK_RESP_H_
+#define __COMMON_MOCK_RESP_H_
+
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "tests/cmocka/common_mock.h"
+
+/* Mock a responder context */
+struct resp_ctx *
+mock_rctx(TALLOC_CTX *mem_ctx,
+          struct tevent_context *ev,
+          struct sss_domain_info *domains,
+          void *pvt_ctx);
+
+/* Mock a client context */
+struct cli_ctx *
+mock_cctx(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx);
+
+/* When mocking a module that calls sss_dp_get_account_{send,recv}
+ * requests, your test, when linked against this module, will call
+ * the mock functions instead. Then you can simulate results of the
+ * sss_dp_get_account_recv call by calling mock_account_recv.
+ *
+ * The mocked sss_sp_get_account_recv shall return the return values
+ * given with parameters dp_err, dp_ret and msg and optionally also call
+ * the acct_cb_t callback, if given with the pvt pointer as user data.
+ * The callback can for instance populate the cache, thus simulating
+ * Data Provider lookup.
+ *
+ * There is also even simpler wrapper called mock_account_recv_simple
+ * that just finishes the account request with a success.
+ */
+typedef int (*acct_cb_t)(void *);
+
+void mock_account_recv(uint16_t dp_err, uint32_t dp_ret, char *msg,
+                       acct_cb_t acct_cb, void *pvt);
+
+void mock_account_recv_simple(void);
+
+#endif /* __COMMON_MOCK_RESP_H_ */
diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
new file mode 100644
index 0000000000000000000000000000000000000000..092e6803eae5e2471e7ff566b8366e83f7b6fb19
--- /dev/null
+++ b/src/tests/cmocka/test_nss_srv.c
@@ -0,0 +1,512 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Common utilities for tests that exercise domains
+
+    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 <tevent.h>
+#include <errno.h>
+#include <popt.h>
+
+#include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_resp.h"
+#include "responder/common/negcache.h"
+#include "responder/nss/nsssrv.h"
+#include "responder/nss/nsssrv_private.h"
+
+#define TESTS_PATH "tests_nss"
+#define TEST_CONF_DB "test_nss_conf.ldb"
+#define TEST_SYSDB_FILE "cache_nss_test.ldb"
+#define TEST_DOM_NAME "nss_test"
+#define TEST_ID_PROVIDER "ldap"
+
+struct nss_test_ctx {
+    struct sss_test_ctx *tctx;
+
+    struct resp_ctx *rctx;
+    struct cli_ctx *cctx;
+    struct sss_cmd_table *nss_cmds;
+    struct nss_ctx *nctx;
+
+    bool ncache_hit;
+};
+
+struct nss_test_ctx *nss_test_ctx;
+
+/* Mock NSS structure */
+struct nss_ctx *
+mock_nctx(TALLOC_CTX *mem_ctx)
+{
+    struct nss_ctx *nctx;
+    errno_t ret;
+
+    nctx = talloc_zero(mem_ctx, struct nss_ctx);
+    if (!nctx) {
+        return NULL;
+    }
+
+    ret = sss_ncache_init(nctx, &nctx->ncache);
+    if (ret != EOK) {
+        talloc_free(nctx);
+        return NULL;
+    }
+    nctx->neg_timeout = 10;
+
+    return nctx;
+}
+
+/* Mock reading requests from a client. Use values passed from mock
+ * instead
+ */
+void __real_sss_packet_get_body(struct sss_packet *packet,
+                                uint8_t **body, size_t *blen);
+
+void __wrap_sss_packet_get_body(struct sss_packet *packet,
+                                uint8_t **body, size_t *blen)
+{
+    enum sss_test_wrapper_call wtype = sss_mock_type(enum sss_test_wrapper_call);
+
+    if (wtype == WRAP_CALL_REAL) {
+        return __real_sss_packet_get_body(packet, body, blen);
+    }
+
+    *body = sss_mock_ptr_type(uint8_t *);
+    *blen = strlen((const char *) *body)+1;
+    return;
+}
+
+/* Mock returning result to client. Terminate the unit test instead. */
+typedef int (*cmd_cb_fn_t)(uint8_t *, size_t );
+
+static void set_cmd_cb(cmd_cb_fn_t fn)
+{
+    will_return(__wrap_sss_cmd_done, fn);
+}
+
+void __wrap_sss_cmd_done(struct cli_ctx *cctx, void *freectx)
+{
+    struct sss_packet *packet = cctx->creq->out;
+    uint8_t *body;
+    size_t blen;
+    cmd_cb_fn_t check_cb;
+
+    check_cb = sss_mock_ptr_type(cmd_cb_fn_t);
+
+    __real_sss_packet_get_body(packet, &body, &blen);
+
+    nss_test_ctx->tctx->error = check_cb(body, blen);
+    nss_test_ctx->tctx->done = true;
+}
+
+enum sss_cli_command __wrap_sss_packet_get_cmd(struct sss_packet *packet)
+{
+    return sss_mock_type(enum sss_cli_command);
+}
+
+int __wrap_sss_cmd_send_empty(struct cli_ctx *cctx, TALLOC_CTX *freectx)
+{
+    nss_test_ctx->tctx->done = true;
+    nss_test_ctx->tctx->error = ENOENT;
+    return EOK;
+}
+
+/* Intercept negative cache lookups */
+int __real_sss_ncache_check_user(struct sss_nc_ctx *ctx, int ttl,
+                                 struct sss_domain_info *dom, const char *name);
+
+int __wrap_sss_ncache_check_user(struct sss_nc_ctx *ctx, int ttl,
+                                 struct sss_domain_info *dom, const char *name)
+{
+    int ret;
+
+    ret = __real_sss_ncache_check_user(ctx, ttl, dom, name);
+    if (ret == EEXIST) {
+        nss_test_ctx->ncache_hit = true;
+    }
+    return ret;
+}
+
+/* Mock input from the client library */
+static void mock_input_user(const char *username)
+{
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+    will_return(__wrap_sss_packet_get_body, username);
+}
+
+static void mock_fill_user(void)
+{
+    /* One packet for the entry and one for num entries */
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+}
+
+static int parse_user_packet(uint8_t *body, size_t blen, struct passwd *pwd)
+{
+    size_t rp = 2 * sizeof(uint32_t);
+
+    SAFEALIGN_COPY_UINT32(&pwd->pw_uid, body+rp, &rp);
+    SAFEALIGN_COPY_UINT32(&pwd->pw_gid, body+rp, &rp);
+
+    /* Sequence of null terminated strings (name, passwd, gecos, dir, shell) */
+    pwd->pw_name = (char *) body+rp;
+    rp += strlen(pwd->pw_name) + 1;
+    if (rp >= blen) return EINVAL;
+
+    pwd->pw_gecos = (char *) body+rp;
+    rp += strlen(pwd->pw_gecos) + 1;
+    if (rp >= blen) return EINVAL;
+
+    pwd->pw_dir = (char *) body+rp;
+    rp += strlen(pwd->pw_dir) + 1;
+    if (rp >= blen) return EINVAL;
+
+    pwd->pw_shell = (char *) body+rp;
+    return EOK;
+}
+
+/* ====================== The tests =============================== */
+
+/* Check getting cached and valid user from cache. Account callback will
+ * not be called and test_nss_getpwnam_check will make sure the user is
+ * the same as the test entered before starting
+ */
+static int test_nss_getpwnam_check(uint8_t *body, size_t blen)
+{
+    struct passwd pwd;
+    errno_t ret;
+
+    ret = parse_user_packet(body, blen, &pwd);
+    assert_int_equal(ret, EOK);
+
+    assert_int_equal(pwd.pw_uid, 123);
+    assert_int_equal(pwd.pw_gid, 456);
+    assert_string_equal(pwd.pw_name, "testuser");
+    assert_string_equal(pwd.pw_shell, "/bin/sh");
+    return EOK;
+}
+
+void test_nss_getpwnam(void **state)
+{
+    errno_t ret;
+
+    /* Prime the cache with a valid user */
+    ret = sysdb_add_user(nss_test_ctx->tctx->sysdb,
+                         nss_test_ctx->tctx->dom,
+                         "testuser", 123, 456, "test user",
+                         "/home/testuser", "/bin/sh", NULL,
+                         NULL, 300, 0);
+    assert_int_equal(ret, EOK);
+
+    mock_input_user("testuser");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM);
+    mock_fill_user();
+
+    /* Query for that user, call a callback when command finishes */
+    set_cmd_cb(test_nss_getpwnam_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+/* Test that searching for a nonexistant user yields ENOENT.
+ * Account callback will be called
+ */
+void test_nss_getpwnam_neg(void **state)
+{
+    errno_t ret;
+
+    mock_input_user("testuser_neg");
+    mock_account_recv_simple();
+
+    assert_true(nss_test_ctx->ncache_hit == false);
+
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with ENOENT */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, ENOENT);
+    assert_true(nss_test_ctx->ncache_hit == false);
+
+    /* Test that subsequent search for a nonexistent user yields
+     * ENOENT and Account callback is not called, on the other hand
+     * the ncache functions will be called
+     */
+    nss_test_ctx->tctx->done = false;
+
+    mock_input_user("testuser_neg");
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with ENOENT */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, ENOENT);
+    /* Negative cache was hit this time */
+    assert_true(nss_test_ctx->ncache_hit == true);
+}
+
+static int test_nss_getpwnam_search_acct_cb(void *pvt)
+{
+    errno_t ret;
+    struct nss_test_ctx *ctx = talloc_get_type(pvt, struct nss_test_ctx);
+
+    ret = sysdb_add_user(ctx->tctx->sysdb,
+                         ctx->tctx->dom,
+                         "testuser_search", 567, 890, "test search",
+                         "/home/testsearch", "/bin/sh", NULL,
+                         NULL, 300, 0);
+    assert_int_equal(ret, EOK);
+
+    return EOK;
+}
+
+static int test_nss_getpwnam_search_check(uint8_t *body, size_t blen)
+{
+    struct passwd pwd;
+    errno_t ret;
+
+    ret = parse_user_packet(body, blen, &pwd);
+    assert_int_equal(ret, EOK);
+
+    assert_int_equal(pwd.pw_uid, 567);
+    assert_int_equal(pwd.pw_gid, 890);
+    assert_string_equal(pwd.pw_name, "testuser_search");
+    assert_string_equal(pwd.pw_shell, "/bin/sh");
+    return EOK;
+}
+
+void test_nss_getpwnam_search(void **state)
+{
+    errno_t ret;
+    struct ldb_result *res;
+
+    mock_input_user("testuser_search");
+    mock_account_recv(0, 0, NULL, test_nss_getpwnam_search_acct_cb, nss_test_ctx);
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM);
+    mock_fill_user();
+    set_cmd_cb(test_nss_getpwnam_search_check);
+
+    ret = sysdb_getpwnam(nss_test_ctx, nss_test_ctx->tctx->sysdb,
+                         nss_test_ctx->tctx->dom, "testuser_search",
+                         &res);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(res->count, 0);
+
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+
+    /* test_nss_getpwnam_search_check will check the user attributes */
+    ret = sysdb_getpwnam(nss_test_ctx, nss_test_ctx->tctx->sysdb,
+                         nss_test_ctx->tctx->dom, "testuser_search",
+                         &res);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(res->count, 1);
+}
+
+/* Test that searching for a user that is expired in the cache goes to the DP
+ * which updates the record and the NSS responder returns the updated record
+ *
+ * The user's shell attribute is updated.
+ */
+static int test_nss_getpwnam_update_acct_cb(void *pvt)
+{
+    errno_t ret;
+    struct nss_test_ctx *ctx = talloc_get_type(pvt, struct nss_test_ctx);
+
+    ret = sysdb_store_user(ctx->tctx->sysdb,
+                           ctx->tctx->dom,
+                           "testuser_update", NULL, 10, 11, "test user",
+                           "/home/testuser", "/bin/ksh", NULL,
+                           NULL, NULL, 300, 0);
+    assert_int_equal(ret, EOK);
+
+    return EOK;
+}
+
+static int test_nss_getpwnam_update_check(uint8_t *body, size_t blen)
+{
+    struct passwd pwd;
+    errno_t ret;
+
+    ret = parse_user_packet(body, blen, &pwd);
+    assert_int_equal(ret, EOK);
+
+    assert_int_equal(pwd.pw_uid, 10);
+    assert_int_equal(pwd.pw_gid, 11);
+    assert_string_equal(pwd.pw_name, "testuser_update");
+    assert_string_equal(pwd.pw_shell, "/bin/ksh");
+    return EOK;
+}
+
+void test_nss_getpwnam_update(void **state)
+{
+    errno_t ret;
+    struct ldb_result *res;
+    const char *shell;
+
+    /* Prime the cache with a valid but expired user */
+    ret = sysdb_add_user(nss_test_ctx->tctx->sysdb,
+                         nss_test_ctx->tctx->dom,
+                         "testuser_update", 10, 11, "test user",
+                         "/home/testuser", "/bin/sh", NULL,
+                         NULL, 1, 1);
+    assert_int_equal(ret, EOK);
+
+    /* Mock client input */
+    mock_input_user("testuser_update");
+    /* Mock client command */
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM);
+    /* Call this function when user is updated by the mock DP request */
+    mock_account_recv(0, 0, NULL, test_nss_getpwnam_update_acct_cb, nss_test_ctx);
+    /* Call this function to check what the responder returned to the client */
+    set_cmd_cb(test_nss_getpwnam_update_check);
+    /* Mock output buffer */
+    mock_fill_user();
+
+    /* Fire the command */
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+
+    /* Check the user was updated in the cache */
+    ret = sysdb_getpwnam(nss_test_ctx, nss_test_ctx->tctx->sysdb,
+                         nss_test_ctx->tctx->dom, "testuser_update",
+                         &res);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(res->count, 1);
+
+    shell = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SHELL, NULL);
+    assert_string_equal(shell, "/bin/ksh");
+}
+
+/* Testsuite setup and teardown */
+void nss_test_setup(void **state)
+{
+    errno_t ret;
+    struct sss_test_conf_param params[] = {
+        { "enumerate", "false" },
+        { NULL, NULL },             /* Sentinel */
+    };
+
+    nss_test_ctx = talloc_zero(NULL, struct nss_test_ctx);
+    assert_non_null(nss_test_ctx);
+
+    nss_test_ctx->tctx = create_dom_test_ctx(nss_test_ctx, TESTS_PATH, TEST_CONF_DB,
+                                             TEST_SYSDB_FILE, TEST_DOM_NAME,
+                                             TEST_ID_PROVIDER, params);
+    assert_non_null(nss_test_ctx->tctx);
+
+    nss_test_ctx->nss_cmds = get_nss_cmds();
+    assert_non_null(nss_test_ctx->nss_cmds);
+
+    /* FIXME - perhaps this should be folded into sssd_domain_init or stricty
+     * used together
+     */
+    ret = sss_names_init(nss_test_ctx, nss_test_ctx->tctx->confdb,
+                         TEST_DOM_NAME, &nss_test_ctx->tctx->dom->names);
+    assert_int_equal(ret, EOK);
+
+    /* Initialize the NSS responder */
+    nss_test_ctx->nctx = mock_nctx(nss_test_ctx);
+    assert_non_null(nss_test_ctx->nctx);
+
+    nss_test_ctx->rctx = mock_rctx(nss_test_ctx, nss_test_ctx->tctx->ev,
+                                   nss_test_ctx->tctx->dom, nss_test_ctx->nctx);
+    assert_non_null(nss_test_ctx->rctx);
+
+    /* Create client context */
+    nss_test_ctx->cctx = mock_cctx(nss_test_ctx, nss_test_ctx->rctx);
+    assert_non_null(nss_test_ctx->cctx);
+}
+
+void nss_test_teardown(void **state)
+{
+    talloc_free(nss_test_ctx);
+}
+
+int main(int argc, const char *argv[])
+{
+    int rv;
+    int no_cleanup = 0;
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0,
+         _("Do not delete the test database after a test run"), NULL },
+        POPT_TABLEEND
+    };
+
+    const UnitTest tests[] = {
+        unit_test_setup_teardown(test_nss_getpwnam,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_getpwnam_neg,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_getpwnam_search,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_getpwnam_update,
+                                 nss_test_setup, nss_test_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_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, TEST_CONF_DB, TEST_SYSDB_FILE);
+    test_dom_suite_setup(TESTS_PATH);
+
+    rv = run_tests(tests);
+    if (rv == 0 && !no_cleanup) {
+        test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_SYSDB_FILE);
+    }
+    return rv;
+}
-- 
1.8.1.4



More information about the sssd-devel mailing list