ldb_mdb/tests: Tests for wrap open
authorGary Lockyer <gary@catalyst.net.nz>
Thu, 8 Mar 2018 03:47:59 +0000 (16:47 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 23 May 2018 00:27:11 +0000 (02:27 +0200)
Tests to ensure that the mdb_env wrapping code correctly handles
multiple ldb's point to the same physical database file.

The test_ldb_close_with_multiple_connections tests are in
ldb_mod_op_test due to the utility code it uses from
elsewhere in that test.

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
lib/ldb/tests/ldb_lmdb_test.c
lib/ldb/tests/ldb_mod_op_test.c
lib/ldb/wscript

index eece2be805708fb5dc8d6edbfb5a4695e74e11c4..f2b04ecd0d7c410890ac9f2cb88e3b85bbe79009 100644 (file)
@@ -55,8 +55,8 @@
 
 #include <sys/wait.h>
 
-#include <lmdb.h>
-
+#include "../ldb_tdb/ldb_tdb.h"
+#include "../ldb_mdb/ldb_mdb.h"
 
 #define TEST_BE  "mdb"
 
@@ -371,6 +371,67 @@ static void test_ldb_add_dn_no_guid_mode(void **state)
        talloc_free(tmp_ctx);
 }
 
+static struct MDB_env *get_mdb_env(struct ldb_context *ldb)
+{
+       void *data = NULL;
+       struct ltdb_private *ltdb = NULL;
+       struct lmdb_private *lmdb = NULL;
+       struct MDB_env *env = NULL;
+
+       data = ldb_module_get_private(ldb->modules);
+       assert_non_null(data);
+
+       ltdb = talloc_get_type(data, struct ltdb_private);
+       assert_non_null(ltdb);
+
+       lmdb = ltdb->lmdb_private;
+       assert_non_null(lmdb);
+
+       env = lmdb->env;
+       assert_non_null(env);
+
+       return env;
+}
+
+static void test_multiple_opens(void **state)
+{
+       struct ldb_context *ldb1 = NULL;
+       struct ldb_context *ldb2 = NULL;
+       struct ldb_context *ldb3 = NULL;
+       struct MDB_env *env1 = NULL;
+       struct MDB_env *env2 = NULL;
+       struct MDB_env *env3 = NULL;
+       int ret;
+       struct ldbtest_ctx *test_ctx = NULL;
+
+       test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
+
+       /*
+        * Open the database again
+        */
+       ldb1 = ldb_init(test_ctx, test_ctx->ev);
+       ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
+       assert_int_equal(ret, 0);
+
+       ldb2 = ldb_init(test_ctx, test_ctx->ev);
+       ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
+       assert_int_equal(ret, 0);
+
+       ldb3 = ldb_init(test_ctx, test_ctx->ev);
+       ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
+       assert_int_equal(ret, 0);
+       /*
+        * We now have 3 ldb's open pointing to the same on disk database
+        * they should all share the same MDB_env
+        */
+       env1 = get_mdb_env(ldb1);
+       env2 = get_mdb_env(ldb2);
+       env3 = get_mdb_env(ldb3);
+
+       assert_ptr_equal(env1, env2);
+       assert_ptr_equal(env1, env3);
+}
+
 int main(int argc, const char **argv)
 {
        const struct CMUnitTest tests[] = {
@@ -394,6 +455,10 @@ int main(int argc, const char **argv)
                        test_ldb_add_dn_no_guid_mode,
                        ldbtest_setup_noguid,
                        ldbtest_teardown),
+               cmocka_unit_test_setup_teardown(
+                       test_multiple_opens,
+                       ldbtest_setup,
+                       ldbtest_teardown),
        };
 
        return cmocka_run_group_tests(tests, NULL, NULL);
index 67ac024db86be8a6249a15f8636352b33cc9dbfc..7b6f19c3a18d16a7c88228b22dcd0ff4958a9a8b 100644 (file)
 #define TEST_BE DEFAULT_BE
 #endif /* TEST_BE */
 
+#ifdef TEST_LMDB
+#include "lmdb.h"
+#include "../ldb_tdb/ldb_tdb.h"
+#include "../ldb_mdb/ldb_mdb.h"
+#endif
+
 struct ldbtest_ctx {
        struct tevent_context *ev;
        struct ldb_context *ldb;
@@ -3820,6 +3826,167 @@ static void test_ldb_talloc_destructor_transaction_cleanup(void **state)
        }
 }
 
+#ifdef TEST_LMDB
+static int test_ldb_multiple_connections_callback(struct ldb_request *req,
+                                                 struct ldb_reply *ares)
+{
+       int ret;
+       int pipes[2];
+       char buf[2];
+       int pid, child_pid;
+       int wstatus;
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               return LDB_SUCCESS;
+
+       case LDB_REPLY_DONE:
+               return ldb_request_done(req, LDB_SUCCESS);
+       }
+
+       {
+               /*
+                * We open a new ldb on an ldb that is already open and
+                * then close it.
+                *
+                * If the multiple connection wrapping is correct the
+                * underlying MDB_env will be left open and we should see
+                * an active reader in the child we fork next
+                */
+               struct ldb_context *ldb = NULL;
+               struct tevent_context *ev = NULL;
+               TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+               ev = tevent_context_init(mem_ctx);
+               assert_non_null(ev);
+
+               ldb = ldb_init(mem_ctx, ev);
+               assert_non_null(ldb);
+
+               ret = ldb_connect(ldb, TEST_BE"://apitest.ldb" , 0, NULL);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               TALLOC_FREE(ldb);
+               TALLOC_FREE(mem_ctx);
+       }
+
+       ret = pipe(pipes);
+       assert_int_equal(ret, 0);
+
+       child_pid = fork();
+       if (child_pid == 0) {
+               struct MDB_env *env = NULL;
+               struct MDB_envinfo stat;
+               close(pipes[0]);
+
+               /*
+                * Check that there are exactly two readers on the MDB file
+                * backing the ldb.
+                *
+                */
+               ret = mdb_env_create(&env);
+               if (ret != 0) {
+                       print_error(__location__
+                                     " mdb_env_create returned (%d)",
+                                     ret);
+                       exit(ret);
+               }
+
+               ret = mdb_env_open(env,
+                                  "apitest.ldb",
+                                  MDB_NOSUBDIR | MDB_NOTLS,
+                                  0644);
+               if (ret != 0) {
+                       print_error(__location__
+                                     " mdb_env_open returned (%d)",
+                                     ret);
+                       exit(ret);
+               }
+
+               ret = mdb_env_info(env, &stat);
+               if (ret != 0) {
+                       print_error(__location__
+                                     " mdb_env_info returned (%d)",
+                                     ret);
+                       exit(ret);
+               }
+               if (stat.me_numreaders != 2) {
+                       print_error(__location__
+                                     " Incorrect number of readers (%d)",
+                                     stat.me_numreaders);
+                       exit(LDB_ERR_CONSTRAINT_VIOLATION);
+               }
+
+               ret = write(pipes[1], "GO", 2);
+               if (ret != 2) {
+                       print_error(__location__
+                                     " write returned (%d)",
+                                     ret);
+                       exit(LDB_ERR_OPERATIONS_ERROR);
+               }
+               exit(LDB_SUCCESS);
+       }
+       close(pipes[1]);
+       ret = read(pipes[0], buf, 2);
+       assert_int_equal(ret, 2);
+
+       pid = waitpid(child_pid, &wstatus, 0);
+       assert_int_equal(pid, child_pid);
+
+       assert_true(WIFEXITED(wstatus));
+
+       assert_int_equal(WEXITSTATUS(wstatus), 0);
+       return LDB_SUCCESS;
+
+}
+
+static void test_ldb_close_with_multiple_connections(void **state)
+{
+       struct search_test_ctx *search_test_ctx = NULL;
+       struct ldb_dn *search_dn = NULL;
+       struct ldb_request *req = NULL;
+       int ret = 0;
+
+       search_test_ctx = talloc_get_type_abort(*state, struct search_test_ctx);
+       assert_non_null(search_test_ctx);
+
+       search_dn = ldb_dn_new_fmt(search_test_ctx,
+                                  search_test_ctx->ldb_test_ctx->ldb,
+                                  "cn=test_search_cn,"
+                                  "dc=search_test_entry");
+       assert_non_null(search_dn);
+
+       /*
+        * The search just needs to call DONE, we don't care about the
+        * contents of the search for this test
+        */
+       ret = ldb_build_search_req(&req,
+                                  search_test_ctx->ldb_test_ctx->ldb,
+                                  search_test_ctx,
+                                  search_dn,
+                                  LDB_SCOPE_SUBTREE,
+                                  "(&(!(filterAttr=*))"
+                                  "(cn=test_search_cn))",
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  test_ldb_multiple_connections_callback,
+                                  NULL);
+       assert_int_equal(ret, 0);
+
+       ret = ldb_request(search_test_ctx->ldb_test_ctx->ldb, req);
+       assert_int_equal(ret, 0);
+
+       ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       assert_int_equal(ret, 0);
+}
+
+#endif
+
 static void test_transaction_start_across_fork(void **state)
 {
        struct ldb_context *ldb1 = NULL;
@@ -4248,6 +4415,12 @@ int main(int argc, const char **argv)
                        test_ldb_talloc_destructor_transaction_cleanup,
                        ldbtest_setup,
                        ldbtest_teardown),
+#ifdef TEST_LMDB
+               cmocka_unit_test_setup_teardown(
+                       test_ldb_close_with_multiple_connections,
+                       ldb_search_test_setup,
+                       ldb_search_test_teardown),
+#endif
                cmocka_unit_test_setup_teardown(
                        test_transaction_start_across_fork,
                        ldbtest_setup,
index 244060f780a0329fc8cea67a4c963a7684100e2d..b8b97191fbb474aee6175911a9cf78ea11bbe327 100644 (file)
@@ -492,8 +492,9 @@ def build(bld):
         if bld.CONFIG_SET('HAVE_LMDB'):
             bld.SAMBA_BINARY('ldb_mdb_mod_op_test',
                              source='tests/ldb_mod_op_test.c',
-                             cflags='-DTEST_BE=\"mdb\" -DGUID_IDX=1',
-                             deps='cmocka ldb',
+                             cflags='-DTEST_BE=\"mdb\" -DGUID_IDX=1 '
+                                  + '-DTEST_LMDB=1',
+                             deps='cmocka ldb lmdb',
                              install=False)
 
             bld.SAMBA_BINARY('ldb_lmdb_test',