summaryrefslogtreecommitdiff
path: root/lib/ldb/tests
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2011-07-05 10:01:32 +1000
committerAndrew Bartlett <abartlet@samba.org>2011-07-05 17:24:47 +1000
commit8420a36dc7fe72fb665e065b8673fa44ff1bbf21 (patch)
tree5350041c1de4cdc73a813949f7cd154c423b3ec5 /lib/ldb/tests
parentc9a6dd56e42beafd297f4aefeb4e00ef9a09073a (diff)
downloadsamba-8420a36dc7fe72fb665e065b8673fa44ff1bbf21.tar.gz
samba-8420a36dc7fe72fb665e065b8673fa44ff1bbf21.tar.bz2
samba-8420a36dc7fe72fb665e065b8673fa44ff1bbf21.zip
ldb: make ldb a top level library for Samba 4.0
Signed-off-by: Andrew Tridgell <tridge@samba.org>
Diffstat (limited to 'lib/ldb/tests')
-rw-r--r--lib/ldb/tests/init.ldif41
-rwxr-xr-xlib/ldb/tests/init_slapd.sh41
-rwxr-xr-xlib/ldb/tests/kill_slapd.sh12
-rwxr-xr-xlib/ldb/tests/ldapi_url.sh11
-rw-r--r--lib/ldb/tests/photo.ldif5
-rwxr-xr-xlib/ldb/tests/python/api.py748
-rw-r--r--lib/ldb/tests/samba4.pngbin0 -> 6239 bytes
-rw-r--r--lib/ldb/tests/sample_module.c71
-rw-r--r--lib/ldb/tests/schema-tests/schema-add-test.ldif66
-rw-r--r--lib/ldb/tests/schema-tests/schema-mod-test-1.ldif5
-rw-r--r--lib/ldb/tests/schema-tests/schema-mod-test-2.ldif5
-rw-r--r--lib/ldb/tests/schema-tests/schema-mod-test-3.ldif5
-rw-r--r--lib/ldb/tests/schema-tests/schema-mod-test-4.ldif5
-rw-r--r--lib/ldb/tests/schema-tests/schema-mod-test-5.ldif5
-rw-r--r--lib/ldb/tests/schema-tests/schema.ldif100
-rw-r--r--lib/ldb/tests/slapd.conf26
-rwxr-xr-xlib/ldb/tests/start_slapd.sh14
-rw-r--r--lib/ldb/tests/test-attribs.ldif6
-rw-r--r--lib/ldb/tests/test-config.ldif67
-rwxr-xr-xlib/ldb/tests/test-controls.sh45
-rw-r--r--lib/ldb/tests/test-default-config.ldif17
-rwxr-xr-xlib/ldb/tests/test-extended.sh69
-rwxr-xr-xlib/ldb/tests/test-generic.sh128
-rw-r--r--lib/ldb/tests/test-index.ldif7
-rwxr-xr-xlib/ldb/tests/test-ldap.sh54
-rw-r--r--lib/ldb/tests/test-modify-modrdn.ldif12
-rw-r--r--lib/ldb/tests/test-modify.ldif23
-rwxr-xr-xlib/ldb/tests/test-schema.sh34
-rwxr-xr-xlib/ldb/tests/test-soloading.sh32
-rwxr-xr-xlib/ldb/tests/test-sqlite3.sh25
-rw-r--r--lib/ldb/tests/test-tdb-features.sh160
-rwxr-xr-xlib/ldb/tests/test-tdb.sh33
-rw-r--r--lib/ldb/tests/test-wildcard.ldif5
-rw-r--r--lib/ldb/tests/test-wrong_attributes.ldif3
-rw-r--r--lib/ldb/tests/test.ldif417
-rw-r--r--lib/ldb/tests/testdata.txt8
-rw-r--r--lib/ldb/tests/testsearch.txt5
37 files changed, 2310 insertions, 0 deletions
diff --git a/lib/ldb/tests/init.ldif b/lib/ldb/tests/init.ldif
new file mode 100644
index 0000000000..97b4561e37
--- /dev/null
+++ b/lib/ldb/tests/init.ldif
@@ -0,0 +1,41 @@
+dn: o=University of Michigan,c=TEST
+objectclass: organization
+objectclass: domainRelatedObject
+l: Ann Arbor, Michigan
+st: Michigan
+o: University of Michigan
+o: UMICH
+o: UM
+o: U-M
+o: U of M
+description: The University of Michigan at Ann Arbor
+postaladdress: University of Michigan $ 535 W. William St. $ Ann Arbor, MI 481
+ 09 $ US
+telephonenumber: +1 313 764-1817
+associateddomain: example.com
+
+# there was an empty "seeAlso" here
+
+dn: ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Ldb Test,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+ou: Ldb Test
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=LdbTspace,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+ou: LdbTspace
+description: test white space removal in comparisons
+uidNumber: 0
+gidNumber: 0
diff --git a/lib/ldb/tests/init_slapd.sh b/lib/ldb/tests/init_slapd.sh
new file mode 100755
index 0000000000..cf06acd08b
--- /dev/null
+++ b/lib/ldb/tests/init_slapd.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+rm -rf tests/tmp/db
+mkdir -p tests/tmp/db
+
+if [ -f tests/tmp/slapd.pid ]; then
+ kill `cat tests/tmp/slapd.pid`
+ sleep 1
+fi
+if [ -f tests/tmp/slapd.pid ]; then
+ kill -9 `cat tests/tmp/slapd.pid`
+ rm -f tests/tmp/slapd.pid
+fi
+
+# we don't consider a slapadd failure as a test suite failure, as it
+# has nothing to do with ldb
+
+MODCONF=tests/tmp/modules.conf
+rm -f $MODCONF
+touch $MODCONF || exit 1
+
+slaptest -u -f $LDBDIR/tests/slapd.conf > /dev/null 2>&1 || {
+ echo "enabling sladp modules"
+cat > $MODCONF <<EOF
+modulepath /usr/lib/ldap
+moduleload back_bdb
+EOF
+}
+
+slaptest -u -f $LDBDIR/tests/slapd.conf || {
+ echo "slaptest failed - skipping ldap tests"
+ exit 0
+}
+
+slapadd -f $LDBDIR/tests/slapd.conf < $LDBDIR/tests/init.ldif || exit 0
+
diff --git a/lib/ldb/tests/kill_slapd.sh b/lib/ldb/tests/kill_slapd.sh
new file mode 100755
index 0000000000..91beb10814
--- /dev/null
+++ b/lib/ldb/tests/kill_slapd.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+if [ -f tests/tmp/slapd.pid ]; then
+ echo "killing slapd process `cat tests/tmp/slapd.pid`"
+ kill -9 `cat tests/tmp/slapd.pid`
+ rm -f tests/tmp/slapd.pid
+fi
diff --git a/lib/ldb/tests/ldapi_url.sh b/lib/ldb/tests/ldapi_url.sh
new file mode 100755
index 0000000000..fef6c35f2b
--- /dev/null
+++ b/lib/ldb/tests/ldapi_url.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# aargh, did LDAP ever have to expose this crap to users ...
+
+BASE=`pwd`
+
+TMPDIR=$BASE/tests/tmp
+
+LDAPI_ESCAPE=`echo $TMPDIR/ldapi | sed 's|/|%2F|g'`
+
+echo "ldapi://$LDAPI_ESCAPE"
diff --git a/lib/ldb/tests/photo.ldif b/lib/ldb/tests/photo.ldif
new file mode 100644
index 0000000000..95ab065672
--- /dev/null
+++ b/lib/ldb/tests/photo.ldif
@@ -0,0 +1,5 @@
+dn: cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+changetype: modify
+add: jpegPhoto
+jpegPhoto:< file://tests/samba4.png
+
diff --git a/lib/ldb/tests/python/api.py b/lib/ldb/tests/python/api.py
new file mode 100755
index 0000000000..e7658d51ab
--- /dev/null
+++ b/lib/ldb/tests/python/api.py
@@ -0,0 +1,748 @@
+#!/usr/bin/env python
+# Simple tests for the ldb python bindings.
+# Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
+
+import os
+import unittest
+
+import ldb
+
+
+def filename():
+ import tempfile
+ try:
+ dir_prefix = os.path.join(os.environ["SELFTEST_PREFIX"], "tmp")
+ except KeyError:
+ dir_prefix = None
+ return tempfile.mktemp(dir=dir_prefix)
+
+class NoContextTests(unittest.TestCase):
+
+ def test_valid_attr_name(self):
+ self.assertTrue(ldb.valid_attr_name("foo"))
+ self.assertFalse(ldb.valid_attr_name("24foo"))
+
+ def test_timestring(self):
+ self.assertEquals("19700101000000.0Z", ldb.timestring(0))
+ self.assertEquals("20071119191012.0Z", ldb.timestring(1195499412))
+
+ def test_string_to_time(self):
+ self.assertEquals(0, ldb.string_to_time("19700101000000.0Z"))
+ self.assertEquals(1195499412, ldb.string_to_time("20071119191012.0Z"))
+
+
+class SimpleLdb(unittest.TestCase):
+
+ def test_connect(self):
+ ldb.Ldb(filename())
+
+ def test_connect_none(self):
+ ldb.Ldb()
+
+ def test_connect_later(self):
+ x = ldb.Ldb()
+ x.connect(filename())
+
+ def test_repr(self):
+ x = ldb.Ldb()
+ self.assertTrue(repr(x).startswith("<ldb connection"))
+
+ def test_set_create_perms(self):
+ x = ldb.Ldb()
+ x.set_create_perms(0600)
+
+ def test_set_modules_dir(self):
+ x = ldb.Ldb()
+ x.set_modules_dir("/tmp")
+
+ def test_modules_none(self):
+ x = ldb.Ldb()
+ self.assertEquals([], x.modules())
+
+ def test_modules_tdb(self):
+ x = ldb.Ldb(filename())
+ self.assertEquals("[<ldb module 'tdb'>]", repr(x.modules()))
+
+ def test_search(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search()), 1)
+
+ def test_search_controls(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search(controls=["paged_results:0:5"])), 1)
+
+ def test_search_attrs(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search(ldb.Dn(l, ""), ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
+
+ def test_search_string_dn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search("", ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
+
+ def test_search_attr_string(self):
+ l = ldb.Ldb(filename())
+ self.assertRaises(TypeError, l.search, attrs="dc")
+
+ def test_opaque(self):
+ l = ldb.Ldb(filename())
+ l.set_opaque("my_opaque", l)
+ self.assertTrue(l.get_opaque("my_opaque") is not None)
+ self.assertEquals(None, l.get_opaque("unknown"))
+
+ def test_search_scope_base(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search(ldb.Dn(l, "dc=foo1"),
+ ldb.SCOPE_ONELEVEL)), 0)
+
+ def test_delete(self):
+ l = ldb.Ldb(filename())
+ self.assertRaises(ldb.LdbError, lambda: l.delete(ldb.Dn(l, "dc=foo2")))
+
+ def test_delete_w_unhandled_ctrl(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo1")
+ m["b"] = ["a"]
+ l.add(m)
+ self.assertRaises(ldb.LdbError, lambda: l.delete(m.dn, ["search_options:1:2"]))
+ l.delete(m.dn)
+
+ def test_contains(self):
+ name = filename()
+ l = ldb.Ldb(name)
+ self.assertFalse(ldb.Dn(l, "dc=foo3") in l)
+ l = ldb.Ldb(name)
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo3")
+ m["b"] = ["a"]
+ l.add(m)
+ try:
+ self.assertTrue(ldb.Dn(l, "dc=foo3") in l)
+ finally:
+ l.delete(m.dn)
+
+ def test_get_config_basedn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(None, l.get_config_basedn())
+
+ def test_get_root_basedn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(None, l.get_root_basedn())
+
+ def test_get_schema_basedn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(None, l.get_schema_basedn())
+
+ def test_get_default_basedn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(None, l.get_default_basedn())
+
+ def test_add(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo4")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo4"))
+
+ def test_add_w_unhandled_ctrl(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo4")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ self.assertRaises(ldb.LdbError, lambda: l.add(m,["search_options:1:2"]))
+
+ def test_add_dict(self):
+ l = ldb.Ldb(filename())
+ m = {"dn": ldb.Dn(l, "dc=foo5"),
+ "bla": "bla"}
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo5"))
+
+ def test_add_dict_string_dn(self):
+ l = ldb.Ldb(filename())
+ m = {"dn": "dc=foo6", "bla": "bla"}
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo6"))
+
+ def test_rename(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo7")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ l.rename(ldb.Dn(l, "dc=foo7"), ldb.Dn(l, "dc=bar"))
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=bar"))
+
+ def test_rename_string_dns(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo8")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ self.assertEquals(len(l.search()), 2)
+ try:
+ l.rename("dc=foo8", "dc=bar")
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=bar"))
+
+ def test_modify_delete(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modifydelete")
+ m["bla"] = ["1234"]
+ l.add(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(["1234"], list(rm["bla"]))
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modifydelete")
+ m["bla"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, "bla")
+ self.assertEquals(ldb.FLAG_MOD_DELETE, m["bla"].flags())
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(1, len(rm))
+ rm = l.search(m.dn, attrs=["bla"])
+ self.assertEquals(0, len(rm))
+ finally:
+ l.delete(ldb.Dn(l, "dc=modifydelete"))
+
+ def test_modify_add(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=add")
+ m["bla"] = ["1234"]
+ l.add(m)
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=add")
+ m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+ self.assertEquals(ldb.FLAG_MOD_ADD, m["bla"].flags())
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(2, len(rm))
+ self.assertEquals(["1234", "456"], list(rm["bla"]))
+ finally:
+ l.delete(ldb.Dn(l, "dc=add"))
+
+ def test_modify_replace(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modify2")
+ m["bla"] = ["1234", "456"]
+ l.add(m)
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modify2")
+ m["bla"] = ldb.MessageElement(["789"], ldb.FLAG_MOD_REPLACE, "bla")
+ self.assertEquals(ldb.FLAG_MOD_REPLACE, m["bla"].flags())
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(2, len(rm))
+ self.assertEquals(["789"], list(rm["bla"]))
+ rm = l.search(m.dn, attrs=["bla"])[0]
+ self.assertEquals(1, len(rm))
+ finally:
+ l.delete(ldb.Dn(l, "dc=modify2"))
+
+ def test_modify_flags_change(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=add")
+ m["bla"] = ["1234"]
+ l.add(m)
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=add")
+ m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+ self.assertEquals(ldb.FLAG_MOD_ADD, m["bla"].flags())
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(2, len(rm))
+ self.assertEquals(["1234", "456"], list(rm["bla"]))
+
+ # Now create another modify, but switch the flags before we do it
+ m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+ m["bla"].set_flags(ldb.FLAG_MOD_DELETE)
+ l.modify(m)
+ rm = l.search(m.dn, attrs=["bla"])[0]
+ self.assertEquals(1, len(rm))
+ self.assertEquals(["1234"], list(rm["bla"]))
+ finally:
+ l.delete(ldb.Dn(l, "dc=add"))
+
+ def test_transaction_commit(self):
+ l = ldb.Ldb(filename())
+ l.transaction_start()
+ m = ldb.Message(ldb.Dn(l, "dc=foo9"))
+ m["foo"] = ["bar"]
+ l.add(m)
+ l.transaction_commit()
+ l.delete(m.dn)
+
+ def test_transaction_cancel(self):
+ l = ldb.Ldb(filename())
+ l.transaction_start()
+ m = ldb.Message(ldb.Dn(l, "dc=foo10"))
+ m["foo"] = ["bar"]
+ l.add(m)
+ l.transaction_cancel()
+ self.assertEquals(0, len(l.search(ldb.Dn(l, "dc=foo10"))))
+
+ def test_set_debug(self):
+ def my_report_fn(level, text):
+ pass
+ l = ldb.Ldb(filename())
+ l.set_debug(my_report_fn)
+
+ def test_zero_byte_string(self):
+ """Testing we do not get trapped in the \0 byte in a property string."""
+ l = ldb.Ldb(filename())
+ l.add({
+ "dn" : "dc=somedn",
+ "objectclass" : "user",
+ "cN" : "LDAPtestUSER",
+ "givenname" : "ldap",
+ "displayname" : "foo\0bar",
+ })
+ res = l.search(expression="(dn=dc=somedn)")
+ self.assertEquals("foo\0bar", res[0]["displayname"][0])
+
+ def test_no_crash_broken_expr(self):
+ l = ldb.Ldb(filename())
+ self.assertRaises(ldb.LdbError,lambda: l.search("", ldb.SCOPE_SUBTREE, "&(dc=*)(dn=*)", ["dc"]))
+
+
+class DnTests(unittest.TestCase):
+
+ def setUp(self):
+ self.ldb = ldb.Ldb(filename())
+
+ def test_set_dn_invalid(self):
+ x = ldb.Message()
+ def assign():
+ x.dn = "astring"
+ self.assertRaises(TypeError, assign)
+
+ def test_eq(self):
+ x = ldb.Dn(self.ldb, "dc=foo11,bar=bloe")
+ y = ldb.Dn(self.ldb, "dc=foo11,bar=bloe")
+ self.assertEquals(x, y)
+ y = ldb.Dn(self.ldb, "dc=foo11,bar=blie")
+ self.assertNotEquals(x, y)
+
+ def test_str(self):
+ x = ldb.Dn(self.ldb, "dc=foo12,bar=bloe")
+ self.assertEquals(x.__str__(), "dc=foo12,bar=bloe")
+
+ def test_repr(self):
+ x = ldb.Dn(self.ldb, "dc=foo13,bla=blie")
+ self.assertEquals(x.__repr__(), "Dn('dc=foo13,bla=blie')")
+
+ def test_get_casefold(self):
+ x = ldb.Dn(self.ldb, "dc=foo14,bar=bloe")
+ self.assertEquals(x.get_casefold(), "DC=FOO14,BAR=bloe")
+
+ def test_validate(self):
+ x = ldb.Dn(self.ldb, "dc=foo15,bar=bloe")
+ self.assertTrue(x.validate())
+
+ def test_parent(self):
+ x = ldb.Dn(self.ldb, "dc=foo16,bar=bloe")
+ self.assertEquals("bar=bloe", x.parent().__str__())
+
+ def test_parent_nonexistant(self):
+ x = ldb.Dn(self.ldb, "@BLA")
+ self.assertEquals(None, x.parent())
+
+ def test_is_valid(self):
+ x = ldb.Dn(self.ldb, "dc=foo18,dc=bloe")
+ self.assertTrue(x.is_valid())
+ x = ldb.Dn(self.ldb, "")
+ self.assertTrue(x.is_valid())
+
+ def test_is_special(self):
+ x = ldb.Dn(self.ldb, "dc=foo19,bar=bloe")
+ self.assertFalse(x.is_special())
+ x = ldb.Dn(self.ldb, "@FOOBAR")
+ self.assertTrue(x.is_special())
+
+ def test_check_special(self):
+ x = ldb.Dn(self.ldb, "dc=foo20,bar=bloe")
+ self.assertFalse(x.check_special("FOOBAR"))
+ x = ldb.Dn(self.ldb, "@FOOBAR")
+ self.assertTrue(x.check_special("@FOOBAR"))
+
+ def test_len(self):
+ x = ldb.Dn(self.ldb, "dc=foo21,bar=bloe")
+ self.assertEquals(2, len(x))
+ x = ldb.Dn(self.ldb, "dc=foo21")
+ self.assertEquals(1, len(x))
+
+ def test_add_child(self):
+ x = ldb.Dn(self.ldb, "dc=foo22,bar=bloe")
+ self.assertTrue(x.add_child(ldb.Dn(self.ldb, "bla=bloe")))
+ self.assertEquals("bla=bloe,dc=foo22,bar=bloe", x.__str__())
+
+ def test_add_base(self):
+ x = ldb.Dn(self.ldb, "dc=foo23,bar=bloe")
+ base = ldb.Dn(self.ldb, "bla=bloe")
+ self.assertTrue(x.add_base(base))
+ self.assertEquals("dc=foo23,bar=bloe,bla=bloe", x.__str__())
+
+ def test_add(self):
+ x = ldb.Dn(self.ldb, "dc=foo24")
+ y = ldb.Dn(self.ldb, "bar=bla")
+ self.assertEquals("dc=foo24,bar=bla", str(y + x))
+
+ def test_parse_ldif(self):
+ msgs = self.ldb.parse_ldif("dn: foo=bar\n")
+ msg = msgs.next()
+ self.assertEquals("foo=bar", str(msg[1].dn))
+ self.assertTrue(isinstance(msg[1], ldb.Message))
+ ldif = self.ldb.write_ldif(msg[1], ldb.CHANGETYPE_NONE)
+ self.assertEquals("dn: foo=bar\n\n", ldif)
+
+ def test_parse_ldif_more(self):
+ msgs = self.ldb.parse_ldif("dn: foo=bar\n\n\ndn: bar=bar")
+ msg = msgs.next()
+ self.assertEquals("foo=bar", str(msg[1].dn))
+ msg = msgs.next()
+ self.assertEquals("bar=bar", str(msg[1].dn))
+
+ def test_canonical_string(self):
+ x = ldb.Dn(self.ldb, "dc=foo25,bar=bloe")
+ self.assertEquals("/bloe/foo25", x.canonical_str())
+
+ def test_canonical_ex_string(self):
+ x = ldb.Dn(self.ldb, "dc=foo26,bar=bloe")
+ self.assertEquals("/bloe\nfoo26", x.canonical_ex_str())
+
+
+class LdbMsgTests(unittest.TestCase):
+
+ def setUp(self):
+ self.msg = ldb.Message()
+
+ def test_init_dn(self):
+ self.msg = ldb.Message(ldb.Dn(ldb.Ldb(), "dc=foo27"))
+ self.assertEquals("dc=foo27", str(self.msg.dn))
+
+ def test_iter_items(self):
+ self.assertEquals(0, len(self.msg.items()))
+ self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "dc=foo28")
+ self.assertEquals(1, len(self.msg.items()))
+
+ def test_repr(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "dc=foo29")
+ self.msg["dc"] = "foo"
+ self.assertEquals("Message({'dn': Dn('dc=foo29'), 'dc': MessageElement(['foo'])})", repr(self.msg))
+
+ def test_len(self):
+ self.assertEquals(0, len(self.msg))
+
+ def test_notpresent(self):
+ self.assertRaises(KeyError, lambda: self.msg["foo"])
+
+ def test_del(self):
+ del self.msg["foo"]
+
+ def test_add(self):
+ self.msg.add(ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla"))
+
+ def test_elements_empty(self):
+ self.assertEquals([], self.msg.elements())
+
+ def test_elements(self):
+ el = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+ self.msg.add(el)
+ self.assertEquals([el], self.msg.elements())
+
+ def test_add_value(self):
+ self.assertEquals(0, len(self.msg))
+ self.msg["foo"] = ["foo"]
+ self.assertEquals(1, len(self.msg))
+
+ def test_add_value_multiple(self):
+ self.assertEquals(0, len(self.msg))
+ self.msg["foo"] = ["foo", "bla"]
+ self.assertEquals(1, len(self.msg))
+ self.assertEquals(["foo", "bla"], list(self.msg["foo"]))
+
+ def test_set_value(self):
+ self.msg["foo"] = ["fool"]
+ self.assertEquals(["fool"], list(self.msg["foo"]))
+ self.msg["foo"] = ["bar"]
+ self.assertEquals(["bar"], list(self.msg["foo"]))
+
+ def test_keys(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
+ self.msg["foo"] = ["bla"]
+ self.msg["bar"] = ["bla"]
+ self.assertEquals(["dn", "foo", "bar"], self.msg.keys())
+
+ def test_dn(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
+ self.assertEquals("@BASEINFO", self.msg.dn.__str__())
+
+ def test_get_dn(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
+ self.assertEquals("@BASEINFO", self.msg.get("dn").__str__())
+
+ def test_get_invalid(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
+ self.assertRaises(TypeError, self.msg.get, 42)
+
+ def test_get_other(self):
+ self.msg["foo"] = ["bar"]
+ self.assertEquals("bar", self.msg.get("foo")[0])
+
+ def test_get_default(self):
+ self.assertEquals(None, self.msg.get("tatayoyo"))
+
+ self.assertEquals("anniecordie", self.msg.get("tatayoyo", "anniecordie"))
+
+ def test_get_unknown(self):
+ self.assertEquals(None, self.msg.get("lalalala"))
+
+ def test_msg_diff(self):
+ l = ldb.Ldb()
+ msgs = l.parse_ldif("dn: foo=bar\nfoo: bar\nbaz: do\n\ndn: foo=bar\nfoo: bar\nbaz: dont\n")
+ msg1 = msgs.next()[1]
+ msg2 = msgs.next()[1]
+ msgdiff = l.msg_diff(msg1, msg2)
+ self.assertEquals("foo=bar", msgdiff.get("dn").__str__())
+ self.assertRaises(KeyError, lambda: msgdiff["foo"])
+ self.assertEquals(1, len(msgdiff))
+
+ def test_equal_empty(self):
+ msg1 = ldb.Message()
+ msg2 = ldb.Message()
+ self.assertEquals(msg1, msg2)
+
+ def test_equal_simplel(self):
+ db = ldb.Ldb(filename())
+ msg1 = ldb.Message()
+ msg1.dn = ldb.Dn(db, "foo=bar")
+ msg2 = ldb.Message()
+ msg2.dn = ldb.Dn(db, "foo=bar")
+ self.assertEquals(msg1, msg2)
+ msg1['foo'] = 'bar'
+ msg2['foo'] = 'bar'
+ self.assertEquals(msg1, msg2)
+ msg2['foo'] = 'blie'
+ self.assertNotEquals(msg1, msg2)
+ msg2['foo'] = 'blie'
+
+ def test_from_dict(self):
+ rec = {"dn": "dc=fromdict",
+ "a1": ["a1-val1", "a1-val1"]}
+ l = ldb.Ldb()
+ # check different types of input Flags
+ for flags in [ldb.FLAG_MOD_ADD, ldb.FLAG_MOD_REPLACE, ldb.FLAG_MOD_DELETE]:
+ m = ldb.Message.from_dict(l, rec, flags)
+ self.assertEquals(rec["a1"], list(m["a1"]))
+ self.assertEquals(flags, m["a1"].flags())
+ # check input params
+ self.assertRaises(TypeError, ldb.Message.from_dict, dict(), rec, ldb.FLAG_MOD_REPLACE)
+ self.assertRaises(TypeError, ldb.Message.from_dict, l, list(), ldb.FLAG_MOD_REPLACE)
+ self.assertRaises(ValueError, ldb.Message.from_dict, l, rec, 0)
+ # Message.from_dict expects dictionary with 'dn'
+ err_rec = {"a1": ["a1-val1", "a1-val1"]}
+ self.assertRaises(TypeError, ldb.Message.from_dict, l, err_rec, ldb.FLAG_MOD_REPLACE)
+
+
+
+class MessageElementTests(unittest.TestCase):
+
+ def test_cmp_element(self):
+ x = ldb.MessageElement(["foo"])
+ y = ldb.MessageElement(["foo"])
+ z = ldb.MessageElement(["bzr"])
+ self.assertEquals(x, y)
+ self.assertNotEquals(x, z)
+
+ def test_create_iterable(self):
+ x = ldb.MessageElement(["foo"])
+ self.assertEquals(["foo"], list(x))
+
+ def test_repr(self):
+ x = ldb.MessageElement(["foo"])
+ self.assertEquals("MessageElement(['foo'])", repr(x))
+ x = ldb.MessageElement(["foo", "bla"])
+ self.assertEquals(2, len(x))
+ self.assertEquals("MessageElement(['foo','bla'])", repr(x))
+
+ def test_get_item(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals("foo", x[0])
+ self.assertEquals("bar", x[1])
+ self.assertEquals("bar", x[-1])
+ self.assertRaises(IndexError, lambda: x[45])
+
+ def test_len(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals(2, len(x))
+
+ def test_eq(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ y = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals(y, x)
+ x = ldb.MessageElement(["foo"])
+ self.assertNotEquals(y, x)
+ y = ldb.MessageElement(["foo"])
+ self.assertEquals(y, x)
+
+ def test_extended(self):
+ el = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+ self.assertEquals("MessageElement(['456'])", repr(el))
+
+
+class ModuleTests(unittest.TestCase):
+
+ def test_register_module(self):
+ class ExampleModule:
+ name = "example"
+ ldb.register_module(ExampleModule)
+
+ def test_use_module(self):
+ ops = []
+ class ExampleModule:
+ name = "bla"
+
+ def __init__(self, ldb, next):
+ ops.append("init")
+ self.next = next
+
+ def search(self, *args, **kwargs):
+ return self.next.search(*args, **kwargs)
+
+ def request(self, *args, **kwargs):
+ pass
+
+ name = filename()
+ ldb.register_module(ExampleModule)
+ if os.path.exists(name):
+ os.unlink(name)
+ l = ldb.Ldb(name)
+ l.add({"dn": "@MODULES", "@LIST": "bla"})
+ self.assertEquals([], ops)
+ l = ldb.Ldb(name)
+ self.assertEquals(["init"], ops)
+
+class LdbResultTests(unittest.TestCase):
+
+ def setUp(self):
+ name = filename()
+ self.name = name
+ if os.path.exists(name):
+ os.unlink(name)
+ self.l = ldb.Ldb(name)
+ self.l.add({"dn": "DC=SAMBA,DC=ORG", "name": "samba.org"})
+ self.l.add({"dn": "OU=ADMIN,DC=SAMBA,DC=ORG", "name": "Admins"})
+ self.l.add({"dn": "OU=USERS,DC=SAMBA,DC=ORG", "name": "Users"})
+ self.l.add({"dn": "OU=OU1,DC=SAMBA,DC=ORG", "name": "OU #1"})
+ self.l.add({"dn": "OU=OU2,DC=SAMBA,DC=ORG", "name": "OU #2"})
+ self.l.add({"dn": "OU=OU3,DC=SAMBA,DC=ORG", "name": "OU #3"})
+ self.l.add({"dn": "OU=OU4,DC=SAMBA,DC=ORG", "name": "OU #4"})
+ self.l.add({"dn": "OU=OU5,DC=SAMBA,DC=ORG", "name": "OU #5"})
+ self.l.add({"dn": "OU=OU6,DC=SAMBA,DC=ORG", "name": "OU #6"})
+ self.l.add({"dn": "OU=OU7,DC=SAMBA,DC=ORG", "name": "OU #7"})
+ self.l.add({"dn": "OU=OU8,DC=SAMBA,DC=ORG", "name": "OU #8"})
+ self.l.add({"dn": "OU=OU9,DC=SAMBA,DC=ORG", "name": "OU #9"})
+ self.l.add({"dn": "OU=OU10,DC=SAMBA,DC=ORG", "name": "OU #10"})
+
+ def tearDown(self):
+ if os.path.exists(self.name):
+ os.unlink(self.name)
+
+ def test_return_type(self):
+ res = self.l.search()
+ self.assertEquals(str(res), "<ldb result>")
+
+ def test_get_msgs(self):
+ res = self.l.search()
+ list = res.msgs
+
+ def test_get_controls(self):
+ res = self.l.search()
+ list = res.controls
+
+ def test_get_referals(self):
+ res = self.l.search()
+ list = res.referals
+
+ def test_iter_msgs(self):
+ found = False
+ for l in self.l.search().msgs:
+ if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
+ found = True
+ self.assertTrue(found)
+
+ def test_iter_msgs_count(self):
+ self.assertTrue(self.l.search().count > 0)
+ # 13 objects has been added to the DC=SAMBA, DC=ORG
+ self.assertEqual(self.l.search(base="DC=SAMBA,DC=ORG").count, 13)
+
+ def test_iter_controls(self):
+ res = self.l.search().controls
+ it = iter(res)
+
+ def test_create_control(self):
+ self.assertRaises(ValueError, ldb.Control, self.l, "tatayoyo:0")
+ c = ldb.Control(self.l, "relax:1")
+ self.assertEquals(c.critical, True)
+ self.assertEquals(c.oid, "1.3.6.1.4.1.4203.666.5.12")
+
+ def test_iter_refs(self):
+ res = self.l.search().referals
+ it = iter(res)
+
+ def test_iter_as_sequence_msgs(self):
+ found = False
+ res = self.l.search().msgs
+
+ for i in range(0, len(res)):
+ l = res[i]
+ if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
+ found = True
+ self.assertTrue(found)
+
+ def test_iter_as_sequence(self):
+ found = False
+ res = self.l.search()
+
+ for i in range(0, len(res)):
+ l = res[i]
+ if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
+ found = True
+ self.assertTrue(found)
+
+class VersionTests(unittest.TestCase):
+
+ def test_version(self):
+ self.assertTrue(isinstance(ldb.__version__, str))
+
+
+if __name__ == '__main__':
+ import unittest
+ unittest.TestProgram()
diff --git a/lib/ldb/tests/samba4.png b/lib/ldb/tests/samba4.png
new file mode 100644
index 0000000000..c8096889a6
--- /dev/null
+++ b/lib/ldb/tests/samba4.png
Binary files differ
diff --git a/lib/ldb/tests/sample_module.c b/lib/ldb/tests/sample_module.c
new file mode 100644
index 0000000000..bee40a5e80
--- /dev/null
+++ b/lib/ldb/tests/sample_module.c
@@ -0,0 +1,71 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "ldb_module.h"
+
+static int sample_add(struct ldb_module *mod, struct ldb_request *req)
+{
+ struct ldb_control *control;
+
+ ldb_msg_add_fmt(req->op.add.message, "touchedBy", "sample");
+
+ /* check if there's a relax control */
+ control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(mod, req);
+ } else {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+}
+
+static int sample_modify(struct ldb_module *mod, struct ldb_request *req)
+{
+ struct ldb_control *control;
+
+ /* check if there's a relax control */
+ control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(mod, req);
+ } else {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+}
+
+
+static struct ldb_module_ops ldb_sample_module_ops = {
+ .name = "sample",
+ .add = sample_add,
+ .del = sample_modify,
+ .modify = sample_modify,
+};
+
+int ldb_sample_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_sample_module_ops);
+}
diff --git a/lib/ldb/tests/schema-tests/schema-add-test.ldif b/lib/ldb/tests/schema-tests/schema-add-test.ldif
new file mode 100644
index 0000000000..472ab48fac
--- /dev/null
+++ b/lib/ldb/tests/schema-tests/schema-add-test.ldif
@@ -0,0 +1,66 @@
+dn: CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: container
+cn: Users
+description: Default container for upgraded user accounts
+instanceType: 4
+whenCreated: 20050116175504.0Z
+whenChanged: 20050116175504.0Z
+uSNCreated: 1
+uSNChanged: 1
+showInAdvancedViewOnly: FALSE
+name: Users
+objectGUID: b847056a-9934-d87b-8a1a-99fabe0863c8
+systemFlags: 0x8c000000
+objectCategory: CN=Container,CN=Schema,CN=Configuration,DC=schema,DC=test
+isCriticalSystemObject: TRUE
+nTSecurityDescriptor: foo
+
+dn: CN=Administrator,CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: Administrator
+description: Built-in account for administering the computer/domain
+instanceType: 4
+whenCreated: 20050116175504.0Z
+whenChanged: 20050116175504.0Z
+uSNCreated: 1
+memberOf: CN=Group Policy Creator Owners,CN=Users,DC=schema,DC=test
+memberOf: CN=Domain Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Enterprise Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Schema Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Administrators,CN=Builtin,DC=schema,DC=test
+uSNChanged: 1
+name: Administrator
+objectGUID: 6c02f98c-46c6-aa38-5f13-a510cac04e6c
+userAccountControl: 0x10200
+badPwdCount: 0
+codePage: 0
+countryCode: 0
+badPasswordTime: 0
+lastLogoff: 0
+lastLogon: 0
+pwdLastSet: 0
+primaryGroupID: 513
+objectSid: S-1-5-21-43662522-77495566-38969261-500
+adminCount: 1
+accountExpires: 9223372036854775807
+logonCount: 0
+sAMAccountName: Administrator
+sAMAccountType: 0x30000000
+objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=schema,DC=test
+isCriticalSystemObject: TRUE
+unicodePwd: samba
+nTSecurityDescriptor: foo
+
+dn: CN=Test,CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: test
+cn: Test
+description: This is a test
+objectCategory: CN=Test,CN=Schema,CN=Configuration,DC=schema,DC=test
+nTSecurityDescriptor: foo
+instanceType: 4
+
diff --git a/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif b/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif
new file mode 100644
index 0000000000..b976724485
--- /dev/null
+++ b/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+replace: description
+description: this test must not fail
+
diff --git a/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif b/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif
new file mode 100644
index 0000000000..fa193af683
--- /dev/null
+++ b/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+delete: description
+# this test must not fail
+
diff --git a/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif b/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif
new file mode 100644
index 0000000000..8ab7798f9c
--- /dev/null
+++ b/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+add: description
+description: this test must not fail
+
diff --git a/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif b/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif
new file mode 100644
index 0000000000..cbf0e60bbe
--- /dev/null
+++ b/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+add: foo
+foo: this test must fail
+
diff --git a/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif b/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif
new file mode 100644
index 0000000000..bc64e9edb6
--- /dev/null
+++ b/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+delete: nTSecurityDescriptor
+# this test must fail
+
diff --git a/lib/ldb/tests/schema-tests/schema.ldif b/lib/ldb/tests/schema-tests/schema.ldif
new file mode 100644
index 0000000000..4ab1932839
--- /dev/null
+++ b/lib/ldb/tests/schema-tests/schema.ldif
@@ -0,0 +1,100 @@
+dn: @INDEXLIST
+@IDXATTR: name
+@IDXATTR: sAMAccountName
+@IDXATTR: objectSid
+@IDXATTR: objectClass
+@IDXATTR: member
+@IDXATTR: uidNumber
+@IDXATTR: gidNumber
+@IDXATTR: unixName
+@IDXATTR: privilege
+@IDXATTR: lDAPDisplayName
+
+dn: @ATTRIBUTES
+realm: CASE_INSENSITIVE
+userPrincipalName: CASE_INSENSITIVE
+servicePrincipalName: CASE_INSENSITIVE
+name: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+sAMAccountName: CASE_INSENSITIVE
+objectClass: CASE_INSENSITIVE
+unicodePwd: HIDDEN
+ntPwdHash: HIDDEN
+ntPwdHistory: HIDDEN
+lmPwdHash: HIDDEN
+lmPwdHistory: HIDDEN
+createTimestamp: HIDDEN
+modifyTimestamp: HIDDEN
+
+dn: @MODULES
+@LIST: timestamps,schema
+
+dn: CN=Top,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+lDAPDisplayName: top
+cn: Top
+uSNCreated: 1
+uSNChanged: 1
+subClassOf: top
+systemMustContain: objectClass
+systemMayContain: structuralObjectClass
+systemMayContain: createTimeStamp
+systemMayContain: modifyTimeStamp
+systemMayContain: creatorsName
+systemMayContain: modifiersName
+systemMayContain: hasSubordinates
+systemMayContain: subschemaSubentry
+systemMayContain: collectiveSubentry
+systemMayContain: entryUUID
+systemMayContain: entryCSN
+systemMayContain: namingCSN
+systemMayContain: superiorUUID
+systemMayContain: contextCSN
+systemMayContain: whenCreated
+systemMayContain: whenChanged
+systemMayContain: uSNCreated
+systemMayContain: uSNChanged
+systemMayContain: distinguishedName
+systemMayContain: name
+systemMayContain: cn
+systemMayContain: userPassword
+systemMayContain: labeledURI
+
+dn: CN=Class-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+lDAPDisplayName: classSchema
+cn: Class-Schema
+uSNCreated: 2
+uSNChanged: 2
+lDAPDisplayName: classSchema
+subClassOf: top
+systemMustContain: cn
+systemMustContain: subClassOf
+systemMayContain: systemPossSuperiors
+systemMayContain: systemOnly
+systemMayContain: systemMustContain
+systemMayContain: systemMayContain
+systemMayContain: systemAuxiliaryClass
+systemMayContain: possSuperiors
+systemMayContain: mustContain
+systemMayContain: mayContain
+systemMayContain: lDAPDisplayName
+systemMayContain: auxiliaryClass
+
+dn: CN=Attribute-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+cn: Attribute-Schema
+uSNCreated: 3
+uSNChanged: 3
+lDAPDisplayName: attributeSchema
+subClassOf: top
+systemMustContain: oMSyntax
+systemMustContain: lDAPDisplayName
+systemMustContain: isSingleValued
+systemMustContain: cn
+systemMustContain: attributeSyntax
+systemMustContain: attributeID
+
diff --git a/lib/ldb/tests/slapd.conf b/lib/ldb/tests/slapd.conf
new file mode 100644
index 0000000000..fa2789d8c1
--- /dev/null
+++ b/lib/ldb/tests/slapd.conf
@@ -0,0 +1,26 @@
+loglevel 0
+
+include tests/schema/core.schema
+include tests/schema/cosine.schema
+include tests/schema/inetorgperson.schema
+include tests/schema/openldap.schema
+include tests/schema/nis.schema
+
+
+pidfile tests/tmp/slapd.pid
+argsfile tests/tmp/slapd.args
+
+access to * by * write
+
+allow update_anon bind_anon_dn
+
+include tests/tmp/modules.conf
+
+defaultsearchbase "o=University of Michigan,c=TEST"
+
+backend bdb
+database bdb
+suffix "o=University of Michigan,c=TEST"
+directory tests/tmp/db
+index objectClass eq
+index uid eq
diff --git a/lib/ldb/tests/start_slapd.sh b/lib/ldb/tests/start_slapd.sh
new file mode 100755
index 0000000000..11679d47a3
--- /dev/null
+++ b/lib/ldb/tests/start_slapd.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+mkdir -p $LDBDIR/tests/tmp/db
+
+# running slapd in the background (with &) means it stays in the same process group, so it can be
+# killed by timelimit
+slapd -d0 -f $LDBDIR/tests/slapd.conf -h "`$LDBDIR/tests/ldapi_url.sh`" $* &
+
+sleep 2
diff --git a/lib/ldb/tests/test-attribs.ldif b/lib/ldb/tests/test-attribs.ldif
new file mode 100644
index 0000000000..79508c4b7b
--- /dev/null
+++ b/lib/ldb/tests/test-attribs.ldif
@@ -0,0 +1,6 @@
+dn: @ATTRIBUTES
+uid: CASE_INSENSITIVE
+cn: CASE_INSENSITIVE
+ou: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+
diff --git a/lib/ldb/tests/test-config.ldif b/lib/ldb/tests/test-config.ldif
new file mode 100644
index 0000000000..7926a9e3c5
--- /dev/null
+++ b/lib/ldb/tests/test-config.ldif
@@ -0,0 +1,67 @@
+##############################
+# global configuration options
+dn: cn=Global,cn=Config,cn=Samba
+objectclass: globalconfig
+LocalConfigCn: cn=%U,cn=Config,cn=Samba
+LocalConfigCn;1: cn=%U,cn=Config,cn=Samba
+LocalConfigCn;2: cn=%I,cn=Config,cn=Samba
+LocalConfigCn;3: cn=%M,cn=Config,cn=Samba
+
+#############
+dn: cn=Protocol,cn=Global,cn=Config,cn=Samba
+maxXmit: 7000
+
+################################
+dn: cn=Volker,cn=Config,cn=Samba
+Workgroup: VNET3
+UnixCharset: UTF8
+Security: user
+Interfaces: vmnet* eth*
+NetbiosName: blu
+GuestAccount: tridge
+
+#################################
+dn: cn=Volker,cn=Config,cn=Samba
+Workgroup: VNET3
+UnixCharset: UTF8
+Security: user
+Interfaces: vmnet* eth*
+NetbiosName: blu
+GuestAccount: tridge
+Include: cn=%U,cn=MyConfig,cn=Config,cn=Samba
+
+#### ((objectClass=fileshare)(cn=test))
+##############################
+# [test] share
+dn: cn=test,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: test
+Comment: a test share
+Path: /home/tridge/samba4/prefix/test
+ReadOnly: no
+
+#####################################
+# [msdn] a remote proxy share, stored
+# on \\msdn\test
+dn: cn=msdn,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: msdn
+NtvfsHandler: cifs
+ReadOnly: no
+_CifsServer: msdn
+_CifsUser: administrator
+_CifsPassword: penguin
+_CifsDomain: winxp
+_CifsShare: test
+
+
+##############################
+# [VisualC] share
+dn: cn=visualc,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: VisualC
+Comment: VisualC development
+Path: /home/tridge/VisualC
+ReadOnly: no
+NtvfsHandler: simple
+
diff --git a/lib/ldb/tests/test-controls.sh b/lib/ldb/tests/test-controls.sh
new file mode 100755
index 0000000000..771085ec60
--- /dev/null
+++ b/lib/ldb/tests/test-controls.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+ LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+ LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+
+echo "LDB_URL: $LDB_URL"
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: @MODULES
+@LIST: sample
+EOF
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: dc=bar
+dc: bar
+someThing: someThingElse
+EOF
+
+$VALGRIND ldbsearch "(touchedBy=sample)" | grep "touchedBy: sample" || exit 1
+# This action are expected to fails because the sample module return an error when presented the relax control
+
+cat <<EOF | $VALGRIND ldbadd --controls "relax:0" && exit 1
+dn: dc=foobar
+dc: foobar
+someThing: someThingElse
+EOF
+
+cat <<EOF | $VALGRIND ldbmodify --controls "relax:0" && exit 1
+dn: dc=bar
+changetype: modify
+replace someThing
+someThing: someThingElseBetter
+EOF
+
+$VALGRIND ldbsearch --controls "bypassoperational:0" >/dev/null 2>&1 || exit 1
+
+set
diff --git a/lib/ldb/tests/test-default-config.ldif b/lib/ldb/tests/test-default-config.ldif
new file mode 100644
index 0000000000..87b7bcd3cc
--- /dev/null
+++ b/lib/ldb/tests/test-default-config.ldif
@@ -0,0 +1,17 @@
+##############################
+# global configuration options
+dn: cn=Global,cn=DefaultConfig,cn=Samba
+objectclass: globalconfig
+Workgroup: WORKGROUP
+UnixCharset: UTF8
+Security: user
+NetbiosName: blu
+GuestAccount: nobody
+
+##############################
+# [_default_] share
+dn: cn=_default_,cn=Shares,cn=DefaultConfig,cn=Samba
+objectclass: fileshare
+cn: _default_
+Path: /tmp
+ReadOnly: yes
diff --git a/lib/ldb/tests/test-extended.sh b/lib/ldb/tests/test-extended.sh
new file mode 100755
index 0000000000..14b988e3f9
--- /dev/null
+++ b/lib/ldb/tests/test-extended.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+echo "Running extended search tests"
+
+mv $LDB_URL $LDB_URL.1
+
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: cn=testrec1,cn=TEST
+i1: 1
+i2: 0
+i3: 1234
+i4: 0x7003004
+
+dn: cn=testrec2,cn=TEST
+i1: 0x800000
+
+dn: cn=testrec3,cn=TEST
+i1: 0x101010101
+i1: 7
+
+dn: cn=auser1,cn=TEST
+groupType: 2147483648
+samAccountType: 805306368
+
+dn: cn=auser2,cn=TEST
+groupType: 2147483648
+samAccountType: 805306369
+
+dn: cn=auser3,cn=TEST
+groupType: 2147483649
+samAccountType: 805306370
+
+dn: cn=auser4,cn=TEST
+groupType: 2147483649
+samAccountType: 805306369
+EOF
+
+checkcount() {
+ count=$1
+ expression="$2"
+ n=`$VALGRIND ldbsearch$EXEEXT "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ $VALGRIND ldbsearch$EXEEXT "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+checkcount 1 '(i3=1234)'
+checkcount 0 '(i3=12345)'
+
+checkcount 2 '(i1:1.2.840.113556.1.4.803:=1)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=3)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=7)'
+checkcount 0 '(i1:1.2.840.113556.1.4.803:=15)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=0x800000)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=8388608)'
+
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=1)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=3)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=7)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=15)'
+checkcount 1 '(i1:1.2.840.113556.1.4.804:=0x800000)'
+checkcount 1 '(i1:1.2.840.113556.1.4.804:=8388608)'
+
+# this is one that w2k gives
+checkcount 3 '(|(|(&(!(groupType:1.2.840.113556.1.4.803:=1))(groupType:1.2.840.113556.1.4.803:=2147483648)(groupType:1.2.840.113556.1.4.804:=10))(samAccountType=805306368))(samAccountType=805306369))'
+
diff --git a/lib/ldb/tests/test-generic.sh b/lib/ldb/tests/test-generic.sh
new file mode 100755
index 0000000000..ae9ff7344a
--- /dev/null
+++ b/lib/ldb/tests/test-generic.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+if [ -z "$LDB_SPECIALS" ]; then
+ LDB_SPECIALS=1
+ export LDB_SPECIALS
+fi
+
+echo "LDB_URL: $LDB_URL"
+
+echo "Adding base elements"
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test.ldif || exit 1
+
+echo "Adding again - should fail"
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test.ldif 2> /dev/null && {
+ echo "Should have failed to add again - gave $?"
+ exit 1
+}
+
+echo "Modifying elements"
+$VALGRIND ldbmodify$EXEEXT $LDBDIR/tests/test-modify.ldif || exit 1
+
+echo "Showing modified record"
+$VALGRIND ldbsearch$EXEEXT '(uid=uham)' || exit 1
+
+echo "Rename entry with ldbmodify - modrdn"
+$VALGRIND ldbmodify$EXEEXT $LDBDIR/tests/test-modify-modrdn.ldif || exit 1
+
+echo "Rename entry with ldbrename"
+OLDDN="cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST"
+NEWDN="cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST"
+$VALGRIND ldbrename$EXEEXT "$OLDDN" "$NEWDN" || exit 1
+
+echo "Showing renamed record"
+$VALGRIND ldbsearch$EXEEXT '(uid=uham)' || exit 1
+
+echo "Starting ldbtest"
+$VALGRIND ldbtest$EXEEXT --num-records 100 --num-searches 10 || exit 1
+
+if [ $LDB_SPECIALS = 1 ]; then
+ echo "Adding index"
+ $VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test-index.ldif || exit 1
+fi
+
+echo "Adding bad attributes - should fail"
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test-wrong_attributes.ldif && {
+ echo "Should fhave failed - gave $?"
+ exit 1
+}
+
+echo "Testing indexed search"
+$VALGRIND ldbsearch$EXEEXT '(uid=uham)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(objectclass=person)(objectclass=person)(objectclass=top))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(uid=uham)(uid=uham))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(|(uid=uham)(uid=uham))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(|(uid=uham)(uid=uham)(objectclass=OpenLDAPperson))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(uid=uham)(uid=uham)(!(objectclass=xxx)))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(objectclass=person)(uid=uham)(!(uid=uhamxx)))' uid \* \+ dn || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(uid=uham)(uid=uha*)(title=*))' uid || exit 1
+
+# note that the "((" is treated as an attribute not an expression
+# this matches the openldap ldapsearch behaviour of looking for a '='
+# to see if the first argument is an expression or not
+$VALGRIND ldbsearch$EXEEXT '((' uid || exit 1
+$VALGRIND ldbsearch$EXEEXT '(objectclass=)' uid || exit 1
+$VALGRIND ldbsearch$EXEEXT -b 'cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' -s base "" sn || exit 1
+
+echo "Test wildcard match"
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test-wildcard.ldif || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=test*multi)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=*test*multi*)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=*test_multi)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=test_multi*)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=test*multi*test*multi)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=test*multi*test*multi*multi_*)' || exit 1
+
+echo "Starting ldbtest indexed"
+$VALGRIND ldbtest$EXEEXT --num-records 100 --num-searches 500 || exit 1
+
+echo "Testing one level search"
+count=`$VALGRIND ldbsearch$EXEEXT -b 'ou=Groups,o=University of Michigan,c=TEST' -s one 'objectclass=*' none |grep '^dn' | wc -l`
+if [ $count != 3 ]; then
+ echo returned $count records - expected 3
+ exit 1
+fi
+
+echo "Testing binary file attribute value"
+$VALGRIND ldbmodify$EXEEXT $LDBDIR/tests/photo.ldif || exit 1
+count=`$VALGRIND ldbsearch$EXEEXT '(cn=Hampster Ursula)' jpegPhoto | grep '^dn' | wc -l`
+if [ $count != 1 ]; then
+ echo returned $count records - expected 1
+ exit 1
+fi
+
+echo "*TODO* Testing UTF8 upper lower case searches !!"
+
+echo "Testing compare"
+count=`$VALGRIND ldbsearch$EXEEXT '(cn>=t)' cn | grep '^dn' | wc -l`
+if [ $count != 2 ]; then
+ echo returned $count records - expected 2
+ echo "this fails on openLdap ..."
+fi
+
+count=`$VALGRIND ldbsearch$EXEEXT '(cn<=t)' cn | grep '^dn' | wc -l`
+if [ $count != 13 ]; then
+ echo returned $count records - expected 13
+ echo "this fails on openLdap ..."
+fi
+
+checkcount() {
+ count=$1
+ scope=$2
+ basedn=$3
+ expression="$4"
+ n=`$VALGRIND ldbsearch$EXEEXT -s "$scope" -b "$basedn" "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ bin/ldbsearch "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+checkcount 0 'base' '' '(uid=uham)'
+checkcount 0 'one' '' '(uid=uham)'
+
+checkcount 1 'base' 'cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' '(uid=uham)'
+checkcount 1 'one' 'ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' '(uid=uham)'
+checkcount 1 'one' 'ou=People,o=University of Michigan,c=TEST' '(ou=ldb test)'
diff --git a/lib/ldb/tests/test-index.ldif b/lib/ldb/tests/test-index.ldif
new file mode 100644
index 0000000000..268173641d
--- /dev/null
+++ b/lib/ldb/tests/test-index.ldif
@@ -0,0 +1,7 @@
+dn: @INDEXLIST
+@IDXATTR: uid
+@IDXATTR: objectclass
+
+dn: @ATTRIBUTES
+uid: CASE_INSENSITIVE
+
diff --git a/lib/ldb/tests/test-ldap.sh b/lib/ldb/tests/test-ldap.sh
new file mode 100755
index 0000000000..14cfb5f979
--- /dev/null
+++ b/lib/ldb/tests/test-ldap.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+PATH=/usr/local/sbin:/usr/sbin:/sbin:$PATH
+export PATH
+SCHEMA_NEEDED="core nis cosine inetorgperson openldap"
+
+# setup needed schema files
+for f in $SCHEMA_NEEDED; do
+ if [ ! -r tests/schema/$f.schema ]; then
+ mkdir -p tests/schema
+ if [ -r /etc/ldap/schema/$f.schema ]; then
+ ln -s /etc/ldap/schema/$f.schema tests/schema/$f.schema
+ continue;
+ fi
+ if [ -r /etc/openldap/schema/$f.schema ]; then
+ ln -s /etc/openldap/schema/$f.schema tests/schema/$f.schema
+ continue;
+ fi
+
+ echo "SKIPPING TESTS: you need the following OpenLDAP schema files"
+ for f in $SCHEMA_NEEDED; do
+ echo " $f.schema"
+ done
+ exit 0
+ fi
+done
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+LDB_URL=`$LDBDIR/tests/ldapi_url.sh`
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+LDB_SPECIALS=0
+export LDB_SPECIALS
+
+if $LDBDIR/tests/init_slapd.sh &&
+ $LDBDIR/tests/start_slapd.sh &&
+ $LDBDIR/tests/test-generic.sh; then
+ echo "ldap tests passed";
+ ret=0
+else
+ echo "ldap tests failed";
+ ret=$?
+fi
+
+#$LDBDIR/tests/kill_slapd.sh
+
+exit $ret
diff --git a/lib/ldb/tests/test-modify-modrdn.ldif b/lib/ldb/tests/test-modify-modrdn.ldif
new file mode 100644
index 0000000000..efa3149462
--- /dev/null
+++ b/lib/ldb/tests/test-modify-modrdn.ldif
@@ -0,0 +1,12 @@
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+changetype: moddn
+newrdn: cn=Hampster Ursula
+deleteoldrdn: 1
+newsuperior: ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+
+dn: cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+changetype: modrdn
+newrdn: cn=Ursula Hampster
+deleteoldrdn: 1
diff --git a/lib/ldb/tests/test-modify.ldif b/lib/ldb/tests/test-modify.ldif
new file mode 100644
index 0000000000..e5b9ca4086
--- /dev/null
+++ b/lib/ldb/tests/test-modify.ldif
@@ -0,0 +1,23 @@
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+changetype: modify
+add: drink
+drink: mango lassi
+-
+add: drink
+drink: lemonade
+-
+delete: pager
+-
+replace: telephonenumber
+telephonenumber: +61 2 6260 6012
+telephonenumber: +61 412 666 929
+-
+delete: telephonenumber
+telephonenumber: +61 2 6260 6012
+-
+delete: telephonenumber
+telephonenumber: +61 412 666 929
+-
+add: telephonenumber
+telephonenumber: +61 412 666 929
diff --git a/lib/ldb/tests/test-schema.sh b/lib/ldb/tests/test-schema.sh
new file mode 100755
index 0000000000..97841844db
--- /dev/null
+++ b/lib/ldb/tests/test-schema.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+LDB_URL="tdb://schema.ldb"
+export LDB_URL
+
+rm -f schema.ldb
+
+echo "LDB_URL: $LDB_URL"
+
+echo "Adding schema"
+$VALGRIND bin/ldbadd $LDBDIR/tests/schema-tests/schema.ldif || exit 1
+
+echo "Adding few test elements (no failure expected here)"
+$VALGRIND bin/ldbadd $LDBDIR/tests/schema-tests/schema-add-test.ldif || exit 1
+
+echo "Modifying elements (2 failures expected here)"
+
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-1.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-2.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-3.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-4.ldif
+if [ "$?" = "0" ]; then
+ echo "test failed!"
+ exit 1
+fi
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-5.ldif
+if [ "$?" = "0" ]; then
+ echo "test failed!"
+ exit 1
+fi
+
+echo "Showing modified record"
+$VALGRIND bin/ldbsearch '(cn=Test)' || exit 1
+
diff --git a/lib/ldb/tests/test-soloading.sh b/lib/ldb/tests/test-soloading.sh
new file mode 100755
index 0000000000..da6d57541e
--- /dev/null
+++ b/lib/ldb/tests/test-soloading.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+ LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+ LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: @MODULES
+@LIST: sample
+EOF
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: dc=bar
+dc: bar
+someThing: someThingElse
+EOF
+
+$VALGRIND ldbsearch "(touchedBy=sample)" | grep "touchedBy: sample" || exit 1
+
diff --git a/lib/ldb/tests/test-sqlite3.sh b/lib/ldb/tests/test-sqlite3.sh
new file mode 100755
index 0000000000..0cef318d98
--- /dev/null
+++ b/lib/ldb/tests/test-sqlite3.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+
+LDB_URL="sqlite3://sqltest.ldb"
+export LDB_URL
+
+rm -f sqltest.ldb
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+PATH=bin:$PATH
+export PATH
+
+LDB_SPECIALS=0
+export LDB_SPECIALS
+
+$LDBDIR/tests/test-generic.sh
+
+#. $LDBDIR/tests/test-extended.sh
+
+#. $LDBDIR/tests/test-tdb-features.sh
+
diff --git a/lib/ldb/tests/test-tdb-features.sh b/lib/ldb/tests/test-tdb-features.sh
new file mode 100644
index 0000000000..d4248366ca
--- /dev/null
+++ b/lib/ldb/tests/test-tdb-features.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+
+echo "Running tdb feature tests"
+
+mv $LDB_URL $LDB_URL.2
+
+checkcount() {
+ count=$1
+ expression="$2"
+ n=`$VALGRIND ldbsearch$EXEEXT "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ $VALGRIND ldbsearch$EXEEXT "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+echo "Testing case sensitive search"
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: cn=t1,cn=TEST
+objectClass: testclass
+test: foo
+EOF
+checkcount 1 '(test=foo)'
+checkcount 0 '(test=FOO)'
+checkcount 0 '(test=FO*)'
+
+echo "Making case insensitive"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: add
+add: test
+test: CASE_INSENSITIVE
+EOF
+
+echo $ldif | $VALGRIND ldbmodify$EXEEXT || exit 1
+checkcount 1 '(test=foo)'
+checkcount 1 '(test=FOO)'
+checkcount 1 '(test=fo*)'
+
+echo "adding i"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: cn=t1,cn=TEST
+changetype: modify
+add: i
+i: 0x100
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 0 '(i=256)'
+
+echo "marking i as INTEGER"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+add: i
+i: INTEGER
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 1 '(i=256)'
+
+echo "adding j"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: cn=t1,cn=TEST
+changetype: modify
+add: j
+j: 0x100
+EOF
+checkcount 1 '(j=0x100)'
+checkcount 0 '(j=256)'
+
+echo "Adding wildcard attribute"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+add: *
+*: INTEGER
+EOF
+checkcount 1 '(j=0x100)'
+checkcount 1 '(j=256)'
+
+echo "Testing class search"
+checkcount 0 '(objectClass=otherclass)'
+checkcount 1 '(objectClass=testclass)'
+
+echo "Adding index"
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: @INDEXLIST
+@IDXATTR: i
+@IDXATTR: test
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 1 '(i=256)'
+checkcount 0 '(i=-256)'
+checkcount 1 '(test=foo)'
+checkcount 1 '(test=FOO)'
+checkcount 1 '(test=*f*o)'
+
+echo "making test case sensitive"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+replace: test
+test: NONE
+EOF
+checkcount 1 '(test=foo)'
+checkcount 0 '(test=FOO)'
+checkcount 1 '(test=f*o*)'
+
+checkone() {
+ count=$1
+ base="$2"
+ expression="$3"
+ n=`$VALGRIND ldbsearch$EXEEXT -s one -b "$base" "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ $VALGRIND ldbsearch$EXEEXT -s one -b "$base" "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+echo "Removing wildcard attribute"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+delete: *
+*: INTEGER
+EOF
+
+echo "Adding one level indexes"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @INDEXLIST
+changetype: modify
+add: @IDXONE
+@IDXONE: 1
+EOF
+
+echo "Testing one level indexed search"
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: cn=one,cn=t1,cn=TEST
+objectClass: oneclass
+cn: one
+test: one
+EOF
+checkone 1 "cn=t1,cn=TEST" '(test=one)'
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: cn=two,cn=t1,cn=TEST
+objectClass: oneclass
+cn: two
+test: one
+
+dn: cn=three,cn=t1,cn=TEST
+objectClass: oneclass
+cn: three
+test: one
+EOF
+checkone 3 "cn=t1,cn=TEST" '(test=one)'
+checkone 1 "cn=t1,cn=TEST" '(cn=two)'
+
diff --git a/lib/ldb/tests/test-tdb.sh b/lib/ldb/tests/test-tdb.sh
new file mode 100755
index 0000000000..9da1e57060
--- /dev/null
+++ b/lib/ldb/tests/test-tdb.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+ LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+ LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: @MODULES
+@LIST: rdn_name
+EOF
+
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/init.ldif || exit 1
+
+. $LDBDIR/tests/test-generic.sh
+
+. $LDBDIR/tests/test-extended.sh
+
+. $LDBDIR/tests/test-tdb-features.sh
+
+. $LDBDIR/tests/test-controls.sh
diff --git a/lib/ldb/tests/test-wildcard.ldif b/lib/ldb/tests/test-wildcard.ldif
new file mode 100644
index 0000000000..222512eeab
--- /dev/null
+++ b/lib/ldb/tests/test-wildcard.ldif
@@ -0,0 +1,5 @@
+dn: cn=test_multi_test_multi_test_multi,o=University of Michigan,c=TEST
+objectclass: person
+cn: test_multi_test_multi_test_multi
+sn: multi_test
+description: test multi wildcards matching
diff --git a/lib/ldb/tests/test-wrong_attributes.ldif b/lib/ldb/tests/test-wrong_attributes.ldif
new file mode 100644
index 0000000000..27f45f0e56
--- /dev/null
+++ b/lib/ldb/tests/test-wrong_attributes.ldif
@@ -0,0 +1,3 @@
+dn: @ATTRIBUTES
+uid: CASE_INTENSIVE
+
diff --git a/lib/ldb/tests/test.ldif b/lib/ldb/tests/test.ldif
new file mode 100644
index 0000000000..fd37f0037e
--- /dev/null
+++ b/lib/ldb/tests/test.ldif
@@ -0,0 +1,417 @@
+dn: ou=Groups,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+#LEAD COMMENT
+
+# another comment
+dn: CN=All Staff,ou=Groups,o=University of Michigan,c=TEST
+#EMBEDDED COMMENT
+member: cn=Manager,o=University of Michigan,c=TEST
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Unive
+ rsity of Michigan,c=TEST
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
+ =US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=University
+ of Michigan,c=TEST
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Univ
+ ersity of Michigan,c=TEST
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
+ igan,c=TEST
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Univers
+ ity of Michigan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=University of Michigan,c=TEST
+member: cn=Manager,o=University of Michigan,c=TEST
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
+ =US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
+ igan,c=TEST
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Universit
+ y of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid:: YmplCW5zZW4
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Ann
+ Arbor, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Ann Arbor, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=University
+ of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Ann Arbor, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Ann Arbor, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+
+dn: cn=ITD Staff,ou=Groups,o=University of Michigan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+uniquemember: cn=Manager,o=University of Michigan,c=TEST
+uniquemember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=U
+ niversity of Michigan,c=TEST
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ o=University of Michigan,c=TEST
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,o=Unive
+ rsity of Michigan,c=TEST
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Ann Arbor, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Universi
+ ty of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 933 Brooks $ Ann Arbor, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Ann Arbor, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Michigan
+ ,c=TEST
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Ann Arbor, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=University of M
+ ichigan,c=TEST
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 912 East Bllvd $ Ann Arbor, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+
+dn: cn=Manager,o=University of Michigan,c=TEST
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michigan,c=
+ TEST
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
+mail: uham@mail.alumni.example.com
+description: a long attribute name, longer than 128 bytes so that we
+ trigger sign extension problems in tdb_pack, no thats not long enough
+ yet, maybe this is. I'll just keep going till it triggers the error
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
+
+dn: cn=Fred Bassett,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Fred Bassett
+sn: Bassett
+uid: Bassett, Fred
diff --git a/lib/ldb/tests/testdata.txt b/lib/ldb/tests/testdata.txt
new file mode 100644
index 0000000000..dadb9f0f98
--- /dev/null
+++ b/lib/ldb/tests/testdata.txt
@@ -0,0 +1,8 @@
+foo=bar5
+(&(|(a=b)(c=d))(e=f))
+(&(|(a=b)(c=d)(g=h))(e=f))
+name=firstname lastname
+(&(sid=S-1-2-3)(name = fred bloggs))
+(&(|(a=b)(c=d))(g=f))
+(&(sid=S-1-2-3)(!(name = fred bloggs)))
+(&(!(|(a=b)(c=d))(g=f)))
diff --git a/lib/ldb/tests/testsearch.txt b/lib/ldb/tests/testsearch.txt
new file mode 100644
index 0000000000..c5738639b7
--- /dev/null
+++ b/lib/ldb/tests/testsearch.txt
@@ -0,0 +1,5 @@
+(blah=foo)
+(objectclass=person)
+(dn=*)
+(&(objectclass=person)(objectclass=person))
+(&(objectclass=person)(objectclass=personx))