#!/usr/bin/python2

import unittest
from ipapython.dn import *

def default_rdn_attr_arg(i):
    return 'a%d' % i

def default_rdn_value_arg(i):
    return str(i)

def alt_rdn_attr_arg(i):
    return 'b%d' % i

def alt_rdn_value_arg(i):
    return str(i*10)

def make_rdn_args(low, high, kind, attr=None, value=None):
    result=[]
    for i in range(low, high):
        if attr is None:
            new_attr = default_rdn_attr_arg(i)
        elif callable(attr):
            new_attr = attr(i)
        else:
            new_attr = attr

        if value is None:
            new_value = default_rdn_value_arg(i)
        elif callable(value):
            new_value = value(i)
        else:
            new_value = value

        if kind == 'tuple':
            result.append((new_attr, new_value))
        elif kind == 'list':
            result.append([new_attr, new_value])
        elif kind == 'RDN':
            result.append(RDN((new_attr, new_value)))
        else:
            raise ValueError("Unknown kind = %s" % kind)

    return result

def expected_class(klass, component):
    if klass is AVA:
        if component == 'self':
            return AVA

    elif klass is EditableAVA:
        if component == 'self':
            return EditableAVA

    elif klass is RDN:
        if component == 'self':
            return RDN
        elif component == 'AVA':
            return AVA

    elif klass is EditableRDN:
        if component == 'self':
            return EditableRDN
        elif component == 'AVA':
            return EditableAVA

    elif klass is DN:
        if component == 'self':
            return DN
        elif component == 'AVA':
            return AVA
        elif component == 'RDN':
            return RDN

    elif klass is EditableDN:
        if component == 'self':
            return EditableDN
        elif component == 'AVA':
            return EditableAVA
        elif component == 'RDN':
            return EditableRDN

    raise ValueError("class %s with component '%s' unknown" % (klass.__name__, component))


class TestAVA(unittest.TestCase):
    def setUp(self):
        self.attr1    = 'cn'
        self.value1   = 'Bob'
        self.str_ava1 = '%s=%s' % (self.attr1, self.value1)
        self.ava1     = AVA(self.attr1, self.value1)

        self.attr2    = 'ou'
        self.value2   = 'People'
        self.str_ava2 = '%s=%s' % (self.attr2, self.value2)
        self.ava2     = AVA(self.attr2, self.value2)

        self.attr3    = 'c'
        self.value3   = 'US'
        self.str_ava3 = '%s=%s' % (self.attr3, self.value3)
        self.ava3     = AVA(self.attr3, self.value3)

    def assertExpectedClass(self, klass, obj, component):
        self.assertIs(obj.__class__, expected_class(klass, component))

    def test_create(self):
        for AVA_class in (AVA, EditableAVA):
            # Create with attr,value pair
            ava1 = AVA_class(self.attr1, self.value1)
            self.assertExpectedClass(AVA_class, ava1, 'self')
            self.assertEqual(ava1, self.ava1)

            # Create with "attr=value" string
            ava1 = AVA_class(self.str_ava1)
            self.assertExpectedClass(AVA_class, ava1, 'self')
            self.assertEqual(ava1, self.ava1)

            # Create with tuple (attr, value)
            ava1 = AVA_class((self.attr1, self.value1))
            self.assertExpectedClass(AVA_class, ava1, 'self')
            self.assertEqual(ava1, self.ava1)

            # Create with list [attr, value]
            ava1 = AVA_class([self.attr1, self.value1])
            self.assertExpectedClass(AVA_class, ava1, 'self')
            self.assertEqual(ava1, self.ava1)

            # Create with no args should fail
            with self.assertRaises(TypeError):
                AVA_class()

            # Create with more than 2 args should fail
            with self.assertRaises(TypeError):
                AVA_class(self.attr1, self.value1, self.attr1)

            # Create with 1 arg which is not string should fail
            with self.assertRaises(TypeError):
                AVA_class(1)

            # Create with malformed AVA_class string should fail
            with self.assertRaises(ValueError):
                AVA_class("cn")

            # Create with non-string parameters, should convert
            ava1 = AVA_class(1, self.value1)
            self.assertExpectedClass(AVA_class, ava1, 'self')
            self.assertEqual(ava1.attr, u'1')

            ava1 = AVA_class((1, self.value1))
            self.assertExpectedClass(AVA_class, ava1, 'self')
            self.assertEqual(ava1.attr, u'1')

            ava1 = AVA_class(self.attr1, 1)
            self.assertExpectedClass(AVA_class, ava1, 'self')
            self.assertEqual(ava1.value, u'1')

            ava1 = AVA_class((self.attr1, 1))
            self.assertExpectedClass(AVA_class, ava1, 'self')
            self.assertEqual(ava1.value, u'1')

    def test_indexing(self):
        for AVA_class in (AVA, EditableAVA):
            ava1 = AVA_class(self.ava1)

            self.assertEqual(ava1[self.attr1], self.value1)

            with self.assertRaises(KeyError):
                ava1['foo']

            with self.assertRaises(TypeError):
                ava1[0]

    def test_properties(self):
        for AVA_class in (AVA, EditableAVA):
            ava1 = AVA_class(self.ava1)

            self.assertEqual(ava1.attr, self.attr1)
            self.assertIsInstance(ava1.attr, unicode)

            self.assertEqual(ava1.value, self.value1)
            self.assertIsInstance(ava1.value, unicode)

    def test_str(self):
        for AVA_class in (AVA, EditableAVA):
            ava1 = AVA_class(self.ava1)

            self.assertEqual(str(ava1), self.str_ava1)
            self.assertIsInstance(str(ava1), str)

    def test_cmp(self):
        for AVA_class in (AVA, EditableAVA):
            # Equality
            ava1 = AVA_class(self.attr1, self.value1)

            self.assertTrue(ava1 == self.ava1)
            self.assertFalse(ava1 != self.ava1)

            self.assertTrue(ava1 == self.str_ava1)
            self.assertFalse(ava1 != self.str_ava1)

            result = cmp(ava1, self.ava1)
            self.assertEqual(result, 0)

            # Upper case attr should still be equal
            ava1 = AVA_class(self.attr1.upper(), self.value1)

            self.assertFalse(ava1.attr == self.attr1)
            self.assertTrue(ava1.value == self.value1)
            self.assertTrue(ava1 == self.ava1)
            self.assertFalse(ava1 != self.ava1)

            result = cmp(ava1, self.ava1)
            self.assertEqual(result, 0)

            # Upper case value should still be equal
            ava1 = AVA_class(self.attr1, self.value1.upper())

            self.assertTrue(ava1.attr == self.attr1)
            self.assertFalse(ava1.value == self.value1)
            self.assertTrue(ava1 == self.ava1)
            self.assertFalse(ava1 != self.ava1)

            result = cmp(ava1, self.ava1)
            self.assertEqual(result, 0)

            # Make ava1's attr greater
            if AVA_class.is_mutable:
                ava1.attr = self.attr1 + "1"
            else:
                with self.assertRaises(AttributeError):
                    ava1.attr = self.attr1 + "1"
                ava1 = AVA_class(self.attr1 + "1", self.value1.upper())

            self.assertFalse(ava1 == self.ava1)
            self.assertTrue(ava1 != self.ava1)

            result = cmp(ava1, self.ava1)
            self.assertEqual(result, 1)

            result = cmp(self.ava1, ava1)
            self.assertEqual(result, -1)

            # Reset ava1's attr, should be equal again
            if AVA_class.is_mutable:
                ava1.attr = self.attr1
            else:
                with self.assertRaises(AttributeError):
                    ava1.attr = self.attr1
                ava1 = AVA_class(self.attr1, self.value1.upper())

            result = cmp(ava1, self.ava1)
            self.assertEqual(result, 0)

            # Make ava1's value greater
            # attr will be equal, this tests secondary comparision component
            if AVA_class.is_mutable:
                ava1.value = self.value1 + "1"
            else:
                with self.assertRaises(AttributeError):
                    ava1.value = self.value1 + "1"
                ava1 = AVA_class(self.attr1, self.value1 + "1")

            result = cmp(ava1, self.ava1)
            self.assertEqual(result, 1)

            result = cmp(self.ava1, ava1)
            self.assertEqual(result, -1)

    def test_hashing(self):
        # create AVA's that are equal but differ in case
        immutable_ava1 = AVA((self.attr1.lower(), self.value1.upper()))
        immutable_ava2 = AVA((self.attr1.upper(), self.value1.lower()))

        mutable_ava1   = EditableAVA((self.attr1.lower(), self.value1.upper()))
        mutable_ava2   = EditableAVA((self.attr1.upper(), self.value1.lower()))

        # Immutable AVA's that are equal should hash to the same value.
        # Mutable AVA's should not be hashable.

        self.assertEqual(immutable_ava1, immutable_ava2)
        self.assertEqual(immutable_ava1, mutable_ava1)
        self.assertEqual(immutable_ava1, mutable_ava2)
        self.assertEqual(mutable_ava1, immutable_ava2)

        # Good, everyone's equal, now verify their hash values

        self.assertEqual(hash(immutable_ava1), hash(immutable_ava2))
        with self.assertRaises(TypeError):
            hash(mutable_ava1)
        with self.assertRaises(TypeError):
            hash(mutable_ava2)

        # Different immutable AVA objects with the same value should
        # map to 1 common key and 1 member in a set. The key and
        # member are based on the object's value.
        #
        # Mutable AVA objects should be unhashable.

        for AVA_class in (AVA, EditableAVA):
            ava1_a = AVA_class(self.ava1)
            ava1_b = AVA_class(self.ava1)

            ava2_a = AVA_class(self.ava2)
            ava2_b = AVA_class(self.ava2)

            ava3_a = AVA_class(self.ava3)
            ava3_b = AVA_class(self.ava3)

            self.assertEqual(ava1_a, ava1_b)
            self.assertEqual(ava2_a, ava2_b)
            self.assertEqual(ava3_a, ava3_b)

            d = dict()
            s = set()

            if AVA_class.is_mutable:
                with self.assertRaises(TypeError):
                    d[ava1_a] = str(ava1_a)
                with self.assertRaises(TypeError):
                    d[ava1_b] = str(ava1_b)
                with self.assertRaises(TypeError):
                    d[ava2_a] = str(ava2_a)
                with self.assertRaises(TypeError):
                    d[ava2_b] = str(ava2_b)

                with self.assertRaises(TypeError):
                    s.add(ava1_a)
                with self.assertRaises(TypeError):
                    s.add(ava1_b)
                with self.assertRaises(TypeError):
                    s.add(ava2_a)
                with self.assertRaises(TypeError):
                    s.add(ava2_b)
            else:
                d[ava1_a] = str(ava1_a)
                d[ava1_b] = str(ava1_b)
                d[ava2_a] = str(ava2_a)
                d[ava2_b] = str(ava2_b)

                s.add(ava1_a)
                s.add(ava1_b)
                s.add(ava2_a)
                s.add(ava2_b)

                self.assertEqual(len(d), 2)
                self.assertEqual(len(s), 2)
                self.assertEqual(sorted(d.keys()), sorted([ava1_a, ava2_a]))
                self.assertEqual(sorted(s), sorted([ava1_a, ava2_a]))

                self.assertTrue(ava1_a in d)
                self.assertTrue(ava1_b in d)
                self.assertTrue(ava2_a in d)
                self.assertTrue(ava2_b in d)
                self.assertFalse(ava3_a in d)
                self.assertFalse(ava3_b in d)

                self.assertTrue(d.has_key(ava1_a))
                self.assertTrue(d.has_key(ava1_b))
                self.assertTrue(d.has_key(ava2_a))
                self.assertTrue(d.has_key(ava2_b))
                self.assertFalse(d.has_key(ava3_a))
                self.assertFalse(d.has_key(ava3_b))

                self.assertTrue(ava1_a in s)
                self.assertTrue(ava1_b in s)
                self.assertTrue(ava2_a in s)
                self.assertTrue(ava2_b in s)
                self.assertFalse(ava3_a in s)
                self.assertFalse(ava3_b in s)

    def test_coerce(self):
        # Coerce an immutable to a mutable
        immutable_ava1 = AVA(self.ava1)
        mutable_ava1 = EditableAVA(immutable_ava1)
        self.assertEqual(mutable_ava1, self.ava1)
        self.assertEqual(mutable_ava1, immutable_ava1)

        # Coerce a mutable to an immutable
        mutable_ava1 = EditableAVA(self.ava1)
        immutable_ava1 = AVA(mutable_ava1)
        self.assertEqual(immutable_ava1, self.ava1)
        self.assertEqual(immutable_ava1, mutable_ava1)

class TestRDN(unittest.TestCase):
    def setUp(self):
        # ava1 must sort before ava2
        self.attr1    = 'cn'
        self.value1   = 'Bob'
        self.str_ava1 = '%s=%s' % (self.attr1, self.value1)
        self.ava1     = AVA(self.attr1, self.value1)

        self.str_rdn1 = '%s=%s' % (self.attr1, self.value1)
        self.rdn1 = RDN((self.attr1, self.value1))

        self.attr2    = 'ou'
        self.value2   = 'people'
        self.str_ava2 = '%s=%s' % (self.attr2, self.value2)
        self.ava2     = AVA(self.attr2, self.value2)

        self.str_rdn2 = '%s=%s' % (self.attr2, self.value2)
        self.rdn2     = RDN((self.attr2, self.value2))

        self.str_ava3 = '%s=%s+%s=%s' % (self.attr1, self.value1, self.attr2, self.value2)

        self.str_rdn3 = '%s=%s+%s=%s' % (self.attr1, self.value1, self.attr2, self.value2)
        self.rdn3 = RDN(self.ava1, self.ava2)

    def assertExpectedClass(self, klass, obj, component):
        self.assertIs(obj.__class__, expected_class(klass, component))

    def test_create(self):
        for RDN_class in (RDN, EditableRDN):
            # Create with single attr,value pair
            rdn1 = RDN_class((self.attr1, self.value1))


            self.assertEqual(len(rdn1), 1)
            self.assertEqual(rdn1, self.rdn1)
            self.assertExpectedClass(RDN_class, rdn1, 'self')
            for i in range(0, len(rdn1)):
                self.assertExpectedClass(RDN_class, rdn1[i], 'AVA')
            self.assertEqual(rdn1[0], self.ava1)

            # Create with multiple attr,value pairs
            rdn3 = RDN_class((self.attr1, self.value1), (self.attr2, self.value2))
            self.assertEqual(len(rdn3), 2)
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')
            self.assertEqual(rdn3[0], self.ava1)
            self.assertEqual(rdn3[1], self.ava2)

            # Create with multiple attr,value pairs passed as lists
            rdn3 = RDN_class([self.attr1, self.value1], [self.attr2, self.value2])
            self.assertEqual(len(rdn3), 2)
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')
            self.assertEqual(rdn3[0], self.ava1)
            self.assertEqual(rdn3[1], self.ava2)

            # Create with multiple attr,value pairs but reverse
            # constructor parameter ordering. RDN canonical ordering
            # should remain the same
            rdn3 = RDN_class((self.attr2, self.value2), (self.attr1, self.value1))
            self.assertEqual(len(rdn3), 2)
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')
            self.assertEqual(rdn3[0], self.ava1)
            self.assertEqual(rdn3[1], self.ava2)

            # Create with single AVA object
            rdn1 = RDN_class(self.ava1)
            self.assertEqual(len(rdn1), 1)
            self.assertEqual(rdn1, self.rdn1)
            self.assertExpectedClass(RDN_class, rdn1, 'self')
            for i in range(0, len(rdn1)):
                self.assertExpectedClass(RDN_class, rdn1[i], 'AVA')
            self.assertEqual(rdn1[0], self.ava1)

            # Create with multiple AVA objects
            rdn3 = RDN_class(self.ava1, self.ava2)
            self.assertEqual(len(rdn3), 2)
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')
            self.assertEqual(rdn3[0], self.ava1)
            self.assertEqual(rdn3[1], self.ava2)


            # Create with multiple AVA objects but reverse constructor
            # parameter ordering.  RDN canonical ordering should remain
            # the same
            rdn3 = RDN_class(self.ava2, self.ava1)
            self.assertEqual(len(rdn3), 2)
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')
            self.assertEqual(rdn3[0], self.ava1)
            self.assertEqual(rdn3[1], self.ava2)

            # Create with single string with 1 AVA
            rdn1 = RDN_class(self.str_rdn1)
            self.assertEqual(len(rdn1), 1)
            self.assertEqual(rdn1, self.rdn1)
            self.assertExpectedClass(RDN_class, rdn1, 'self')
            for i in range(0, len(rdn1)):
                self.assertExpectedClass(RDN_class, rdn1[i], 'AVA')
            self.assertEqual(rdn1[0], self.ava1)

            # Create with single string with 2 AVA's
            rdn3 = RDN_class(self.str_rdn3)
            self.assertEqual(len(rdn3), 2)
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')
            self.assertEqual(rdn3[0], self.ava1)
            self.assertEqual(rdn3[1], self.ava2)

    def test_properties(self):
        for RDN_class in (RDN, EditableRDN):
            rdn1 = RDN_class(self.rdn1)
            rdn2 = RDN_class(self.rdn2)
            rdn3 = RDN_class(self.rdn3)

            self.assertEqual(rdn1.attr, self.attr1)
            self.assertIsInstance(rdn1.attr, unicode)

            self.assertEqual(rdn1.value, self.value1)
            self.assertIsInstance(rdn1.value, unicode)

            self.assertEqual(rdn2.attr, self.attr2)
            self.assertIsInstance(rdn2.attr, unicode)

            self.assertEqual(rdn2.value, self.value2)
            self.assertIsInstance(rdn2.value, unicode)

            self.assertEqual(rdn3.attr, self.attr1)
            self.assertIsInstance(rdn3.attr, unicode)

            self.assertEqual(rdn3.value, self.value1)
            self.assertIsInstance(rdn3.value, unicode)

    def test_str(self):
        for RDN_class in (RDN, EditableRDN):
            rdn1 = RDN_class(self.rdn1)
            rdn2 = RDN_class(self.rdn2)
            rdn3 = RDN_class(self.rdn3)

            self.assertEqual(str(rdn1), self.str_rdn1)
            self.assertIsInstance(str(rdn1), str)

            self.assertEqual(str(rdn2), self.str_rdn2)
            self.assertIsInstance(str(rdn2), str)

            self.assertEqual(str(rdn3), self.str_rdn3)
            self.assertIsInstance(str(rdn3), str)

    def test_cmp(self):
        for RDN_class in (RDN, EditableRDN):
            # Equality
            rdn1 = RDN_class((self.attr1, self.value1))

            self.assertTrue(rdn1 == self.rdn1)
            self.assertFalse(rdn1 != self.rdn1)

            self.assertTrue(rdn1 == self.str_rdn1)
            self.assertFalse(rdn1 != self.str_rdn1)

            result = cmp(rdn1, self.rdn1)
            self.assertEqual(result, 0)

            # Make rdn1's attr greater
            if RDN_class.is_mutable:
                rdn1.attr = self.attr1 + "1"
            else:
                rdn1 = RDN_class((self.attr1 + "1", self.value1))

            self.assertFalse(rdn1 == self.rdn1)
            self.assertTrue(rdn1 != self.rdn1)

            result = cmp(rdn1, self.rdn1)
            self.assertEqual(result, 1)

            result = cmp(self.rdn1, rdn1)
            self.assertEqual(result, -1)

            # Reset rdn1's attr, should be equal again
            if RDN_class.is_mutable:
                rdn1.attr = self.attr1
            else:
                rdn1 = RDN_class((self.attr1, self.value1))

            result = cmp(rdn1, self.rdn1)
            self.assertEqual(result, 0)

            # Make rdn1's value greater
            # attr will be equal, this tests secondary comparision component
            if RDN_class.is_mutable:
                rdn1.value = self.value1 + "1"
            else:
                rdn1 = RDN_class((self.attr1, self.value1 + "1"))

            result = cmp(rdn1, self.rdn1)
            self.assertEqual(result, 1)

            result = cmp(self.rdn1, rdn1)
            self.assertEqual(result, -1)

            # Make sure rdn's with more ava's are greater
            result = cmp(self.rdn1, self.rdn3)
            self.assertEqual(result, -1)
            result = cmp(self.rdn3, self.rdn1)
            self.assertEqual(result, 1)

    def test_indexing(self):
        for RDN_class in (RDN, EditableRDN):
            rdn1 = RDN_class(self.rdn1)
            rdn2 = RDN_class(self.rdn2)
            rdn3 = RDN_class(self.rdn3)

            self.assertEqual(rdn1[0], self.ava1)
            self.assertEqual(rdn1[self.ava1.attr], self.ava1.value)
            with self.assertRaises(KeyError):
                rdn1['foo']

            self.assertEqual(rdn2[0], self.ava2)
            self.assertEqual(rdn2[self.ava2.attr], self.ava2.value)
            with self.assertRaises(KeyError):
                rdn2['foo']

            self.assertEqual(rdn3[0], self.ava1)
            self.assertEqual(rdn3[self.ava1.attr], self.ava1.value)
            self.assertEqual(rdn3[1], self.ava2)
            self.assertEqual(rdn3[self.ava2.attr], self.ava2.value)
            with self.assertRaises(KeyError):
                rdn3['foo']

            self.assertEqual(rdn1.attr, self.attr1)
            self.assertEqual(rdn1.value, self.value1)

            with self.assertRaises(TypeError):
                rdn3[1.0]

            # Slices
            self.assertEqual(rdn3[0:1], [self.ava1])
            self.assertEqual(rdn3[:],   [self.ava1, self.ava2])

    def test_assignments(self):
        for RDN_class in (RDN, EditableRDN):
            rdn = RDN_class((self.attr1, self.value1))
            if RDN_class.is_mutable:
                rdn[0] = self.ava2
                self.assertEqual(rdn, self.rdn2)
            else:
                with self.assertRaises(TypeError):
                    rdn[0] = self.ava2
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            rdn = RDN_class((self.attr1, self.value1))
            if RDN_class.is_mutable:
                rdn[0] = (self.attr2, self.value2)
                self.assertEqual(rdn, self.rdn2)
            else:
                with self.assertRaises(TypeError):
                    rdn[0] = (self.attr2, self.value2)
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            rdn  = RDN_class((self.attr1, self.value1))
            if RDN_class.is_mutable:
                rdn[self.attr1] = self.str_ava2
                self.assertEqual(rdn[0], self.ava2)
            else:
                with self.assertRaises(TypeError):
                    rdn[self.attr1] = self.str_ava2
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            # Can't assign multiples to single entry
            rdn  = RDN_class((self.attr1, self.value1))
            with self.assertRaises(TypeError):
                rdn[self.attr1] = self.str_ava3
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            rdn  = RDN_class((self.attr1, self.value1))
            with self.assertRaises(TypeError):
                rdn[self.attr1] = (self.attr1, self.value1, self.attr2, self.value2)
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            rdn  = RDN_class((self.attr1, self.value1))
            with self.assertRaises(TypeError):
                rdn[self.attr1] = [(self.attr1, self.value1), (self.attr2, self.value2)]
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            # Slices
            rdn  = RDN_class((self.attr1, self.value1))
            self.assertEqual(rdn, self.rdn1)
            if RDN_class.is_mutable:
                rdn[0:1] = [self.ava2]
                self.assertEqual(rdn, self.rdn2)
            else:
                with self.assertRaises(TypeError):
                    rdn[0:1] = [self.ava2]
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            rdn  = RDN_class((self.attr1, self.value1))
            self.assertEqual(rdn, self.rdn1)
            if RDN_class.is_mutable:
                rdn[:] = [(self.attr2, self.value2)]
                self.assertEqual(rdn, self.rdn2)
            else:
                with self.assertRaises(TypeError):
                    rdn[:] = [(self.attr2, self.value2)]
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            rdn  = RDN_class((self.attr1, self.value1))
            self.assertEqual(rdn, self.rdn1)
            if RDN_class.is_mutable:
                rdn[:] = [(self.attr1, self.value1),(self.attr2, self.value2)]
                self.assertEqual(rdn, self.rdn3)
            else:
                with self.assertRaises(TypeError):
                    rdn[:] = [(self.attr1, self.value1),(self.attr2, self.value2)]
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')

            rdn  = RDN_class((self.attr1, self.value1))
            self.assertEqual(rdn, self.rdn1)
            if RDN_class.is_mutable:
                rdn[0:1] = [(self.attr1, self.value1), (self.attr2, self.value2)]
                self.assertEqual(rdn, self.rdn3)
            else:
                with self.assertRaises(TypeError):
                    rdn[0:1] = [(self.attr1, self.value1), (self.attr2, self.value2)]
            self.assertExpectedClass(RDN_class, rdn, 'self')
            for i in range(0, len(rdn)):
                self.assertExpectedClass(RDN_class, rdn[i], 'AVA')


    def test_iter(self):
        for RDN_class in (RDN, EditableRDN):
            rdn1 = RDN_class(self.rdn1)
            rdn2 = RDN_class(self.rdn2)
            rdn3 = RDN_class(self.rdn3)

            self.assertEqual(len(rdn1), 1)
            self.assertEqual(rdn1[:], [self.ava1])
            for i, ava in enumerate(rdn1):
                if i == 0:
                    self.assertEqual(ava, self.ava1)
                else:
                    self.fail("got iteration index %d, but len=%d" % (i, len(rdn1)))

            self.assertEqual(len(rdn2), 1)
            self.assertEqual(rdn2[:], [self.ava2])
            for i, ava in enumerate(rdn2):
                if i == 0:
                    self.assertEqual(ava, self.ava2)
                else:
                    self.fail("got iteration index %d, but len=%d" % (i, len(rdn2)))

            self.assertEqual(len(rdn3), 2)
            self.assertEqual(rdn3[:], [self.ava1, self.ava2])
            for i, ava in enumerate(rdn3):
                if i == 0:
                    self.assertEqual(ava, self.ava1)
                elif i == 1:
                    self.assertEqual(ava, self.ava2)
                else:
                    self.fail("got iteration index %d, but len=%d" % (i, len(rdn3)))


    def test_concat(self):
        for RDN_class in (RDN, EditableRDN):
            rdn1 = RDN_class((self.attr1, self.value1))
            rdn2 = RDN_class((self.attr2, self.value2))

            # in-place addtion

            # Note: If __iadd__ is not available Python will emulate += by
            # replacing the lhs object with the result of __add__ (if available).
            # Thus += works for both immutable and mutable RDN,DN object, the only
            # difference is an immutable without __iadd__ will have a different object
            # on the lhs after the operator evaluates.

            rdn1 += rdn2
            self.assertEqual(rdn1, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn1, 'self')
            for i in range(0, len(rdn1)):
                self.assertExpectedClass(RDN_class, rdn1[i], 'AVA')

            rdn1 = RDN_class((self.attr1, self.value1))
            rdn1 += self.ava2
            self.assertEqual(rdn1, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn1, 'self')
            for i in range(0, len(rdn1)):
                self.assertExpectedClass(RDN_class, rdn1[i], 'AVA')

            rdn1 = RDN_class((self.attr1, self.value1))
            rdn1 += self.str_ava2
            self.assertEqual(rdn1, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn1, 'self')
            for i in range(0, len(rdn1)):
                self.assertExpectedClass(RDN_class, rdn1[i], 'AVA')

            # concatenation
            rdn1 = RDN_class((self.attr1, self.value1))
            rdn3 = rdn1 + rdn2
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')

            rdn3 = rdn1 + self.ava2
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')

            rdn3 = rdn1 + self.str_ava2
            self.assertEqual(rdn3, self.rdn3)
            self.assertExpectedClass(RDN_class, rdn3, 'self')
            for i in range(0, len(rdn3)):
                self.assertExpectedClass(RDN_class, rdn3[i], 'AVA')


    def test_hashing(self):
        # create RDN's that are equal but differ in case
        immutable_rdn1 = RDN((self.attr1.lower(), self.value1.upper()))
        immutable_rdn2 = RDN((self.attr1.upper(), self.value1.lower()))

        mutable_rdn1   = EditableRDN((self.attr1.lower(), self.value1.upper()))
        mutable_rdn2   = EditableRDN((self.attr1.upper(), self.value1.lower()))

        # Immutable RDN's that are equal should hash to the same value.
        # Mutable RDN's should not be hashable.

        self.assertEqual(immutable_rdn1, immutable_rdn2)
        self.assertEqual(immutable_rdn1, mutable_rdn1)
        self.assertEqual(immutable_rdn1, mutable_rdn2)
        self.assertEqual(mutable_rdn1, immutable_rdn2)

        # Good, everyone's equal, now verify their hash values

        self.assertEqual(hash(immutable_rdn1), hash(immutable_rdn2))
        with self.assertRaises(TypeError):
            hash(mutable_rdn1)
        with self.assertRaises(TypeError):
            hash(mutable_rdn2)

    def test_coerce(self):
        # Coerce an immutable to a mutable
        immutable_rdn3 = RDN(self.rdn3)
        mutable_rdn3 = EditableRDN(immutable_rdn3)
        self.assertEqual(mutable_rdn3, self.rdn3)
        self.assertEqual(mutable_rdn3, immutable_rdn3)

        # Coerce a mutable to an immutable
        mutable_rdn3 = EditableRDN(self.rdn3)
        immutable_rdn3 = RDN(mutable_rdn3)
        self.assertEqual(immutable_rdn3, self.rdn3)
        self.assertEqual(immutable_rdn3, mutable_rdn3)

class TestDN(unittest.TestCase):
    def setUp(self):
        # ava1 must sort before ava2
        self.attr1    = 'cn'
        self.value1   = 'Bob'
        self.str_ava1 = '%s=%s' % (self.attr1, self.value1)
        self.ava1     = AVA(self.attr1, self.value1)

        self.str_rdn1 = '%s=%s' % (self.attr1, self.value1)
        self.rdn1     = RDN((self.attr1, self.value1))

        self.attr2    = 'ou'
        self.value2   = 'people'
        self.str_ava2 = '%s=%s' % (self.attr2, self.value2)
        self.ava2     = AVA(self.attr2, self.value2)

        self.str_rdn2 = '%s=%s' % (self.attr2, self.value2)
        self.rdn2     = RDN((self.attr2, self.value2))

        self.str_dn1 = self.str_rdn1
        self.dn1 = DN(self.rdn1)

        self.str_dn2 = self.str_rdn2
        self.dn2 = DN(self.rdn2)

        self.str_dn3 = '%s,%s' % (self.str_rdn1, self.str_rdn2)
        self.dn3 = DN(self.rdn1, self.rdn2)

        self.base_rdn1 = RDN(('dc', 'redhat'))
        self.base_rdn2 = RDN(('dc', 'com'))
        self.base_dn = DN(self.base_rdn1, self.base_rdn2)

        self.container_rdn1 = RDN(('cn', 'sudorules'))
        self.container_rdn2 = RDN(('cn', 'sudo'))
        self.container_dn = DN(self.container_rdn1, self.container_rdn2)

        self.base_container_dn = DN((self.attr1, self.value1),
                                    self.container_dn, self.base_dn)


    def assertExpectedClass(self, klass, obj, component):
        self.assertIs(obj.__class__, expected_class(klass, component))

    def test_create(self):
        for DN_class in (DN, EditableDN):
            # Create with single attr,value pair
            dn1 = DN_class((self.attr1, self.value1))
            self.assertEqual(len(dn1), 1)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
            self.assertIsInstance(dn1[0].attr, unicode)
            self.assertIsInstance(dn1[0].value, unicode)
            self.assertEqual(dn1[0], self.rdn1)

            # Create with single attr,value pair passed as a tuple
            dn1 = DN_class((self.attr1, self.value1))
            self.assertEqual(len(dn1), 1)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn1)

            # Creation with multiple attr,value string pairs should fail
            with self.assertRaises(ValueError):
                dn1 = DN_class(self.attr1, self.value1, self.attr2, self.value2)

            # Create with multiple attr,value pairs passed as tuples & lists
            dn1 = DN_class((self.attr1, self.value1), [self.attr2, self.value2])
            self.assertEqual(len(dn1), 2)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn1)
            self.assertEqual(dn1[1], self.rdn2)

            # Create with multiple attr,value pairs passed as tuple and RDN
            dn1 = DN_class((self.attr1, self.value1), RDN((self.attr2, self.value2)))
            self.assertEqual(len(dn1), 2)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn1)
            self.assertEqual(dn1[1], self.rdn2)

            # Create with multiple attr,value pairs but reverse
            # constructor parameter ordering. RDN ordering should also be
            # reversed because DN's are a ordered sequence of RDN's
            dn1 = DN_class((self.attr2, self.value2), (self.attr1, self.value1))
            self.assertEqual(len(dn1), 2)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn2)
            self.assertEqual(dn1[1], self.rdn1)

            # Create with single RDN object
            dn1 = DN_class(self.rdn1)
            self.assertEqual(len(dn1), 1)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn1)

            # Create with multiple RDN objects, assure ordering is preserved.
            dn1 = DN_class(self.rdn1, self.rdn2)
            self.assertEqual(len(dn1), 2)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn1)
            self.assertEqual(dn1[1], self.rdn2)

            # Create with multiple RDN objects in different order, assure
            # ordering is preserved.
            dn1 = DN_class(self.rdn2, self.rdn1)
            self.assertEqual(len(dn1), 2)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn2)
            self.assertEqual(dn1[1], self.rdn1)

            # Create with single string with 1 RDN
            dn1 = DN_class(self.str_rdn1)
            self.assertEqual(len(dn1), 1)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn1)

            # Create with single string with 2 RDN's
            dn1 = DN_class(self.str_dn3)
            self.assertEqual(len(dn1), 2)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')
                self.assertIsInstance(dn1[i].attr, unicode)
                self.assertIsInstance(dn1[i].value, unicode)
            self.assertEqual(dn1[0], self.rdn1)
            self.assertEqual(dn1[1], self.rdn2)

            # Create with RDN, and 2 DN's (e.g. attr + container + base)
            dn1 = DN_class((self.attr1, self.value1), self.container_dn, self.base_dn)
            self.assertEqual(len(dn1), 5)
            dn_str = ','.join([str(self.rdn1),
                               str(self.container_rdn1), str(self.container_rdn2),
                               str(self.base_rdn1), str(self.base_rdn2)])
            self.assertEqual(str(dn1), dn_str)

    def test_str(self):
        for DN_class in (DN, EditableDN):
            dn1 = DN_class(self.dn1)
            dn2 = DN_class(self.dn2)
            dn3 = DN_class(self.dn3)

            self.assertEqual(str(dn1), self.str_dn1)
            self.assertIsInstance(str(dn1), str)

            self.assertEqual(str(dn2), self.str_dn2)
            self.assertIsInstance(str(dn2), str)

            self.assertEqual(str(dn3), self.str_dn3)
            self.assertIsInstance(str(dn3), str)

    def test_cmp(self):
        for DN_class in (DN, EditableDN):
            # Equality
            dn1 = DN_class((self.attr1, self.value1))

            self.assertTrue(dn1 == self.dn1)
            self.assertFalse(dn1 != self.dn1)

            self.assertTrue(dn1 == self.str_dn1)
            self.assertFalse(dn1 != self.str_dn1)

            result = cmp(dn1, self.dn1)
            self.assertEqual(result, 0)

            # Make dn1's attr greater
            if DN_class.is_mutable:
                dn1[0].attr = self.attr1 + "1"
            else:
                with self.assertRaises(AttributeError):
                    dn1[0].attr = self.attr1 + "1"
                dn1 = DN_class((self.attr1 + "1", self.value1))

            self.assertFalse(dn1 == self.dn1)
            self.assertTrue(dn1 != self.dn1)

            result = cmp(dn1, self.dn1)
            self.assertEqual(result, 1)

            result = cmp(self.dn1, dn1)
            self.assertEqual(result, -1)

            # Reset dn1's attr, should be equal again
            if DN_class.is_mutable:
                dn1[0].attr = self.attr1
            else:
                with self.assertRaises(AttributeError):
                    dn1[0].attr = self.attr1
                dn1 = DN_class((self.attr1, self.value1))

            result = cmp(dn1, self.dn1)
            self.assertEqual(result, 0)

            # Make dn1's value greater
            # attr will be equal, this tests secondary comparision component
            if DN_class.is_mutable:
                dn1[0].value = self.value1 + "1"
            else:
                with self.assertRaises(AttributeError):
                    dn1[0].value = self.value1 + "1"
                dn1 = DN_class((self.attr1, self.value1 + "1"))

            result = cmp(dn1, self.dn1)
            self.assertEqual(result, 1)

            result = cmp(self.dn1, dn1)
            self.assertEqual(result, -1)

            # Make sure dn's with more rdn's are greater
            result = cmp(self.dn1, self.dn3)
            self.assertEqual(result, -1)
            result = cmp(self.dn3, self.dn1)
            self.assertEqual(result, 1)


            # Test startswith, endswith
            container_dn = DN_class(self.container_dn)
            base_container_dn = DN_class(self.base_container_dn)

            self.assertTrue(base_container_dn.startswith(self.rdn1))
            self.assertTrue(base_container_dn.startswith(self.dn1))
            self.assertTrue(base_container_dn.startswith(self.dn1 + container_dn))
            self.assertFalse(base_container_dn.startswith(self.dn2))
            self.assertFalse(base_container_dn.startswith(self.rdn2))
            self.assertTrue(base_container_dn.startswith((self.dn1)))
            self.assertTrue(base_container_dn.startswith((self.rdn1)))
            self.assertFalse(base_container_dn.startswith((self.rdn2)))
            self.assertTrue(base_container_dn.startswith((self.rdn2, self.rdn1)))
            self.assertTrue(base_container_dn.startswith((self.dn1, self.dn2)))

            self.assertTrue(base_container_dn.endswith(self.base_dn))
            self.assertTrue(base_container_dn.endswith(container_dn + self.base_dn))
            self.assertFalse(base_container_dn.endswith(DN(self.base_rdn1)))
            self.assertTrue(base_container_dn.endswith(DN(self.base_rdn2)))
            self.assertTrue(base_container_dn.endswith((DN(self.base_rdn1), DN(self.base_rdn2))))

            # Test "in" membership
            self.assertTrue(self.container_rdn1 in container_dn)
            self.assertTrue(container_dn in container_dn)
            self.assertFalse(self.base_rdn1 in container_dn)

            self.assertTrue(self.container_rdn1 in base_container_dn)
            self.assertTrue(container_dn in base_container_dn)
            self.assertTrue(container_dn + self.base_dn in
                            base_container_dn)
            self.assertTrue(self.dn1 + container_dn + self.base_dn in
                            base_container_dn)
            self.assertTrue(self.dn1 + container_dn + self.base_dn ==
                            base_container_dn)

            self.assertFalse(self.container_rdn1 in self.base_dn)

    def test_indexing(self):
        for DN_class in (DN, EditableDN):
            dn1 = DN_class(self.dn1)
            dn2 = DN_class(self.dn2)
            dn3 = DN_class(self.dn3)

            self.assertEqual(dn1[0], self.rdn1)
            self.assertEqual(dn1[self.rdn1.attr], self.rdn1.value)
            with self.assertRaises(KeyError):
                dn1['foo']

            self.assertEqual(dn2[0], self.rdn2)
            self.assertEqual(dn2[self.rdn2.attr], self.rdn2.value)
            with self.assertRaises(KeyError):
                dn2['foo']

            self.assertEqual(dn3[0], self.rdn1)
            self.assertEqual(dn3[self.rdn1.attr], self.rdn1.value)
            self.assertEqual(dn3[1], self.rdn2)
            self.assertEqual(dn3[self.rdn2.attr], self.rdn2.value)
            with self.assertRaises(KeyError):
                dn3['foo']

            with self.assertRaises(TypeError):
                dn3[1.0]

    def test_assignments(self):
        for DN_class in (DN, EditableDN):
            dn_low = 0
            dn_high = 6

            rdn_args = make_rdn_args(dn_low, dn_high, 'tuple',
                                     default_rdn_attr_arg, default_rdn_value_arg)
            dn1 = DN_class(*rdn_args)

            rdn_args = make_rdn_args(dn_low, dn_high, 'list',
                                     default_rdn_attr_arg, default_rdn_value_arg)
            dn2 = DN_class(*rdn_args)

            rdn_args = make_rdn_args(dn_low, dn_high, 'RDN',
                                     default_rdn_attr_arg, default_rdn_value_arg)
            dn3 = DN_class(*rdn_args)

            self.assertEqual(dn1, dn2)
            self.assertEqual(dn1, dn3)

            for i in range(dn_low, dn_high):
                attr = default_rdn_attr_arg(i)
                value = default_rdn_value_arg(i)

                self.assertEqual(dn1[i].attr, attr)
                self.assertEqual(dn1[i].value, value)
                self.assertEqual(dn1[attr], value)
                self.assertExpectedClass(DN_class, dn1, 'self')
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')

                self.assertEqual(dn2[i].attr, attr)
                self.assertEqual(dn2[i].value, value)
                self.assertEqual(dn2[attr], value)
                self.assertExpectedClass(DN_class, dn2, 'self')
                self.assertExpectedClass(DN_class, dn2[i], 'RDN')
                for j in range(0, len(dn2[i])):
                    self.assertExpectedClass(DN_class, dn2[i][j], 'AVA')

                self.assertEqual(dn3[i].attr, attr)
                self.assertEqual(dn3[i].value, value)
                self.assertEqual(dn3[attr], value)
                self.assertExpectedClass(DN_class, dn3, 'self')
                self.assertExpectedClass(DN_class, dn3[i], 'RDN')
                for j in range(0, len(dn3[i])):
                    self.assertExpectedClass(DN_class, dn3[i][j], 'AVA')


            for i in range(dn_low, dn_high):
                if i % 2:
                    orig_attr = default_rdn_attr_arg(i)
                    attr = alt_rdn_attr_arg(i)
                    value = alt_rdn_value_arg(i)

                    if DN_class.is_mutable:
                        dn1[i] = attr, value
                    else:
                        with self.assertRaises(TypeError):
                            dn1[i] = attr, value

                    if DN_class.is_mutable:
                        dn2[orig_attr] = (attr, value)
                    else:
                        with self.assertRaises(TypeError):
                            dn2[orig_attr] = (attr, value)

                    if DN_class.is_mutable:
                        dn3[i] = RDN((attr, value))
                    else:
                        with self.assertRaises(TypeError):
                            dn3[i] = RDN((attr, value))

            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')

            self.assertExpectedClass(DN_class, dn2, 'self')
            for i in range(0, len(dn2)):
                self.assertExpectedClass(DN_class, dn2[i], 'RDN')
                for j in range(0, len(dn2[i])):
                    self.assertExpectedClass(DN_class, dn2[i][j], 'AVA')

            self.assertExpectedClass(DN_class, dn3, 'self')
            for i in range(0, len(dn3)):
                self.assertExpectedClass(DN_class, dn3[i], 'RDN')
                for j in range(0, len(dn3[i])):
                    self.assertExpectedClass(DN_class, dn3[i][j], 'AVA')


            if DN_class.is_mutable:
                self.assertEqual(dn1, dn2)
                self.assertEqual(dn1, dn3)

                for i in range(dn_low, dn_high):
                    if i % 2:
                        attr = alt_rdn_attr_arg(i)
                        value = alt_rdn_value_arg(i)
                    else:
                        attr = default_rdn_attr_arg(i)
                        value = default_rdn_value_arg(i)
                    self.assertEqual(dn1[i].attr, attr)
                    self.assertEqual(dn1[i].value, value)
                    self.assertEqual(dn1[attr], value)

            # Slices
            slice_low = 2
            slice_high = 4
            slice_interval = range(slice_low, slice_high)

            # Slices
            # Assign via tuple
            rdn_args = make_rdn_args(dn_low, dn_high, 'tuple',
                                     default_rdn_attr_arg, default_rdn_value_arg)
            dn = DN_class(*rdn_args)

            dn_slice = make_rdn_args(slice_low, slice_high, 'tuple',
                                     alt_rdn_attr_arg, alt_rdn_value_arg)

            if DN_class.is_mutable:
                dn[slice_low:slice_high] = dn_slice
                for i in range(dn_low, dn_high):
                    if i in slice_interval:
                        attr = alt_rdn_attr_arg(i)
                        value = alt_rdn_value_arg(i)
                    else:
                        attr = default_rdn_attr_arg(i)
                        value = default_rdn_value_arg(i)
                    self.assertEqual(dn[i].attr, attr)
                    self.assertEqual(dn[i].value, value)
                    self.assertEqual(dn[attr], value)

                query_slice = dn[slice_low:slice_high]
                for i, query_rdn in enumerate(query_slice):
                    slice_rdn = RDN(dn_slice[i])
                    self.assertEqual(slice_rdn, query_rdn)
            else:
                with self.assertRaises(TypeError):
                    dn[slice_low:slice_high] = dn_slice


            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            # insert
            dn = DN_class(self.rdn2)

            if DN_class.is_mutable:
                dn.insert(0, self.rdn1)
                self.assertEqual(dn, self.dn3)
            else:
                with self.assertRaises(AttributeError):
                    dn.insert(0, self.rdn1)

            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')
            dn = DN_class(self.rdn1)

            if DN_class.is_mutable:
                dn.insert(1, (self.attr2, self.value2))
                self.assertEqual(dn, self.dn3)
            else:
                with self.assertRaises(AttributeError):
                    dn.insert(1, (self.attr2, self.value2))

            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            # Slices
            # Assign via RDN
            rdn_args = make_rdn_args(dn_low, dn_high, 'tuple',
                                     default_rdn_attr_arg, default_rdn_value_arg)
            dn = DN_class(*rdn_args)

            dn_slice = make_rdn_args(slice_low, slice_high, 'RDN',
                                     alt_rdn_attr_arg, alt_rdn_value_arg)

            if DN_class.is_mutable:
                dn[slice_low:slice_high] = dn_slice
                for i in range(dn_low, dn_high):
                    if i in slice_interval:
                        attr = alt_rdn_attr_arg(i)
                        value = alt_rdn_value_arg(i)
                    else:
                        attr = default_rdn_attr_arg(i)
                        value = default_rdn_value_arg(i)
                    self.assertEqual(dn[i].value, value)
                    self.assertEqual(dn[attr], value)

                query_slice = dn[slice_low:slice_high]
                for i, query_rdn in enumerate(query_slice):
                    slice_rdn = dn_slice[i]
                    self.assertEqual(slice_rdn, query_rdn)
            else:
                with self.assertRaises(TypeError):
                    dn[slice_low:slice_high] = dn_slice

            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

    def test_iter(self):
        for DN_class in (DN, EditableDN):
            dn1 = DN_class(self.dn1)
            dn2 = DN_class(self.dn2)
            dn3 = DN_class(self.dn3)

            self.assertEqual(len(dn1), 1)
            self.assertEqual(dn1[:], [self.rdn1])
            for i, ava in enumerate(dn1):
                if i == 0:
                    self.assertEqual(ava, self.rdn1)
                else:
                    self.fail("got iteration index %d, but len=%d" % (i, len(self.rdn1)))

            self.assertEqual(len(dn2), 1)
            self.assertEqual(dn2[:], [self.rdn2])
            for i, ava in enumerate(dn2):
                if i == 0:
                    self.assertEqual(ava, self.rdn2)
                else:
                    self.fail("got iteration index %d, but len=%d" % (i, len(self.rdn2)))

            self.assertEqual(len(dn3), 2)
            self.assertEqual(dn3[:], [self.rdn1, self.rdn2])
            for i, ava in enumerate(dn3):
                if i == 0:
                    self.assertEqual(ava, self.rdn1)
                elif i == 1:
                    self.assertEqual(ava, self.rdn2)
                else:
                    self.fail("got iteration index %d, but len=%d" % (i, len(dn3)))


    def test_concat(self):
        for DN_class in (DN, EditableDN):
            dn1 = DN_class((self.attr1, self.value1))
            dn2 = DN_class([self.attr2, self.value2])

            # in-place addtion

            # Note: If __iadd__ is not available Python will emulate += by
            # replacing the lhs object with the result of __add__ (if available).
            # Thus += works for both immutable and mutable RDN,DN object, the only
            # difference is an immutable without __iadd__ will have a different object
            # on the lhs after the operator evaluates.

            dn1 += dn2
            self.assertEqual(dn1, self.dn3)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')


            dn1 = DN_class((self.attr1, self.value1))
            dn1 += self.rdn2
            self.assertEqual(dn1, self.dn3)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')


            dn1 = DN_class((self.attr1, self.value1))
            dn1 += self.dn2
            self.assertEqual(dn1, self.dn3)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')


            dn1 = DN_class((self.attr1, self.value1))
            dn1 += self.str_dn2
            self.assertEqual(dn1, self.dn3)
            self.assertExpectedClass(DN_class, dn1, 'self')
            for i in range(0, len(dn1)):
                self.assertExpectedClass(DN_class, dn1[i], 'RDN')
                for j in range(0, len(dn1[i])):
                    self.assertExpectedClass(DN_class, dn1[i][j], 'AVA')


            # concatenation
            dn1 = DN_class((self.attr1, self.value1))
            dn3 = dn1 + dn2
            self.assertEqual(dn3, self.dn3)
            self.assertExpectedClass(DN_class, dn3, 'self')
            for i in range(0, len(dn3)):
                self.assertExpectedClass(DN_class, dn3[i], 'RDN')
                for j in range(0, len(dn3[i])):
                    self.assertExpectedClass(DN_class, dn3[i][j], 'AVA')


            dn1 = DN_class((self.attr1, self.value1))
            dn3 = dn1 + self.rdn2
            self.assertEqual(dn3, self.dn3)
            self.assertExpectedClass(DN_class, dn3, 'self')
            for i in range(0, len(dn3)):
                self.assertExpectedClass(DN_class, dn3[i], 'RDN')
                for j in range(0, len(dn3[i])):
                    self.assertExpectedClass(DN_class, dn3[i][j], 'AVA')

            dn3 = dn1 + self.str_rdn2
            self.assertEqual(dn3, self.dn3)
            self.assertExpectedClass(DN_class, dn3, 'self')
            for i in range(0, len(dn3)):
                self.assertExpectedClass(DN_class, dn3[i], 'RDN')
                self.assertExpectedClass(DN_class, dn3[i][0], 'AVA')

            dn3 = dn1 + self.str_dn2
            self.assertEqual(dn3, self.dn3)
            self.assertExpectedClass(DN_class, dn3, 'self')
            self.assertExpectedClass(DN_class, dn3, 'self')
            for i in range(0, len(dn3)):
                self.assertExpectedClass(DN_class, dn3[i], 'RDN')
                for j in range(0, len(dn3[i])):
                    self.assertExpectedClass(DN_class, dn3[i][j], 'AVA')

            dn3 = dn1 + self.dn2
            self.assertEqual(dn3, self.dn3)
            self.assertExpectedClass(DN_class, dn3, 'self')
            self.assertExpectedClass(DN_class, dn3, 'self')
            for i in range(0, len(dn3)):
                self.assertExpectedClass(DN_class, dn3[i], 'RDN')
                for j in range(0, len(dn3[i])):
                    self.assertExpectedClass(DN_class, dn3[i][j], 'AVA')

    def test_find(self):
        for DN_class in (DN, EditableDN):
            #        -10 -9  -8     -7  -6  -5  -4     -3  -2  -1
            dn = DN_class('t=0,t=1,cn=bob,t=3,t=4,t=5,cn=bob,t=7,t=8,t=9')
            pat = DN_class('cn=bob')

            # forward
            self.assertEqual(dn.find(pat),          2)
            self.assertEqual(dn.find(pat,  1),      2)
            self.assertEqual(dn.find(pat,  1,  3),  2)
            self.assertEqual(dn.find(pat,  2,  3),  2)
            self.assertEqual(dn.find(pat,  6),      6)

            self.assertEqual(dn.find(pat,  7),     -1)
            self.assertEqual(dn.find(pat,  1,  2), -1)

            with self.assertRaises(ValueError):
                self.assertEqual(dn.index(pat,  7),     -1)
            with self.assertRaises(ValueError):
                self.assertEqual(dn.index(pat,  1,  2), -1)

            # reverse
            self.assertEqual(dn.rfind(pat),          6)
            self.assertEqual(dn.rfind(pat, -4),      6)
            self.assertEqual(dn.rfind(pat,  6),      6)
            self.assertEqual(dn.rfind(pat,  6,  8),  6)
            self.assertEqual(dn.rfind(pat,  6,  8),  6)
            self.assertEqual(dn.rfind(pat, -8),      6)
            self.assertEqual(dn.rfind(pat, -8, -4),  6)
            self.assertEqual(dn.rfind(pat, -8, -5),  2)

            self.assertEqual(dn.rfind(pat,  7),     -1)
            self.assertEqual(dn.rfind(pat, -3),     -1)

            with self.assertRaises(ValueError):
                self.assertEqual(dn.rindex(pat,  7),     -1)
            with self.assertRaises(ValueError):
                self.assertEqual(dn.rindex(pat, -3),     -1)


    def test_replace(self):
        for DN_class in (DN, EditableDN):
            dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=8,t=9')
            pat = DN('cn=bob')
            replacement = DN('cn=bob')

            if DN_class.is_mutable:
                n_replaced = dn.replace(pat, replacement)
                self.assertEqual(n_replaced, 0)
            else:
                with self.assertRaises(AttributeError):
                    n_replaced = dn.replace(pat, replacement)
            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            pat = DN('t=2')
            if DN_class.is_mutable:
                expected_dn = DN('t=0,t=1,cn=bob,t=3,t=4,t=5,t=6,t=7,t=8,t=9')
                n_replaced = dn.replace(pat, replacement)
                self.assertEqual(n_replaced, 1)
                self.assertEqual(dn, expected_dn)
            else:
                with self.assertRaises(AttributeError):
                    n_replaced = dn.replace(pat, replacement)
            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=2,t=9')
            if DN_class.is_mutable:
                expected_dn = DN('t=0,t=1,cn=bob,t=3,t=4,t=5,t=6,t=7,t=2,t=9')
                n_replaced = dn.replace(pat, replacement, 1)
                self.assertEqual(n_replaced, 1)
                self.assertEqual(dn, expected_dn)
            else:
                with self.assertRaises(AttributeError):
                    n_replaced = dn.replace(pat, replacement, 1)
            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=2,t=9')
            if DN_class.is_mutable:
                expected_dn = DN('t=0,t=1,cn=bob,t=3,t=4,t=5,t=6,t=7,t=2,t=9')
                n_replaced = dn.replace(pat, replacement, 1)
                self.assertEqual(n_replaced, 1)
                self.assertEqual(dn, expected_dn)
            else:
                with self.assertRaises(AttributeError):
                    n_replaced = dn.replace(pat, replacement, 1)
            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            replacement = DN('cn=bob,ou=people')

            dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=2,t=9')
            if DN_class.is_mutable:
                expected_dn = DN('t=0,t=1,cn=bob,ou=people,t=3,t=4,t=5,t=6,t=7,t=2,t=9')
                n_replaced = dn.replace(pat, replacement, 1)
                self.assertEqual(n_replaced, 1)
                self.assertEqual(dn, expected_dn)
            else:
                with self.assertRaises(AttributeError):
                    n_replaced = dn.replace(pat, replacement, 1)
            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=2,t=9')
            if DN_class.is_mutable:
                expected_dn = DN('t=0,t=1,cn=bob,ou=people,t=3,t=4,t=5,t=6,t=7,cn=bob,ou=people,t=9')
                n_replaced = dn.replace(pat, replacement)
                self.assertEqual(n_replaced, 2)
                self.assertEqual(dn, expected_dn)
            else:
                with self.assertRaises(AttributeError):
                    n_replaced = dn.replace(pat, replacement)
            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            pat = DN('t=3,t=4')
            replacement = DN('cn=bob')
            dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=8,t=9')
            if DN_class.is_mutable:
                expected_dn = DN('t=0,t=1,t=2,cn=bob,t=5,t=6,t=7,t=8,t=9')
                n_replaced = dn.replace(pat, replacement)
                self.assertEqual(n_replaced, 1)
                self.assertEqual(dn, expected_dn)
            else:
                with self.assertRaises(AttributeError):
                    n_replaced = dn.replace(pat, replacement)
            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

            pat = DN('t=3,t=4')
            replacement = DN('cn=bob,ou=people')
            dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=8,t=9')
            if DN_class.is_mutable:
                expected_dn = DN('t=0,t=1,t=2,cn=bob,ou=people,t=5,t=6,t=7,t=8,t=9')
                n_replaced = dn.replace(pat, replacement)
                self.assertEqual(n_replaced, 1)
                self.assertEqual(dn, expected_dn)
            else:
                with self.assertRaises(AttributeError):
                    n_replaced = dn.replace(pat, replacement)
            self.assertExpectedClass(DN_class, dn, 'self')
            for i in range(0, len(dn)):
                self.assertExpectedClass(DN_class, dn[i], 'RDN')
                for j in range(0, len(dn[i])):
                    self.assertExpectedClass(DN_class, dn[i][j], 'AVA')

    def test_hashing(self):
        # create DN's that are equal but differ in case
        immutable_dn1 = DN((self.attr1.lower(), self.value1.upper()))
        immutable_dn2 = DN((self.attr1.upper(), self.value1.lower()))

        mutable_dn1   = EditableDN((self.attr1.lower(), self.value1.upper()))
        mutable_dn2   = EditableDN((self.attr1.upper(), self.value1.lower()))

        # Immutable DN's that are equal should hash to the same value.
        # Mutable DN's should not be hashable.

        self.assertEqual(immutable_dn1, immutable_dn2)
        self.assertEqual(immutable_dn1, mutable_dn1)
        self.assertEqual(immutable_dn1, mutable_dn2)
        self.assertEqual(mutable_dn1, immutable_dn2)

        # Good, everyone's equal, now verify their hash values

        self.assertEqual(hash(immutable_dn1), hash(immutable_dn2))
        with self.assertRaises(TypeError):
            hash(mutable_dn1)
        with self.assertRaises(TypeError):
            hash(mutable_dn2)

        # Different immutable DN objects with the same value should
        # map to 1 common key and 1 member in a set. The key and
        # member are based on the object's value.
        #
        # Mutable DN objects should be unhashable.

        for DN_class in (DN, EditableDN):
            dn1_a = DN_class(self.dn1)
            dn1_b = DN_class(self.dn1)

            dn2_a = DN_class(self.dn2)
            dn2_b = DN_class(self.dn2)

            dn3_a = DN_class(self.dn3)
            dn3_b = DN_class(self.dn3)

            self.assertEqual(dn1_a, dn1_b)
            self.assertEqual(dn2_a, dn2_b)
            self.assertEqual(dn3_a, dn3_b)

            d = dict()
            s = set()

            if DN_class.is_mutable:
                with self.assertRaises(TypeError):
                    d[dn1_a] = str(dn1_a)
                with self.assertRaises(TypeError):
                    d[dn1_b] = str(dn1_b)
                with self.assertRaises(TypeError):
                    d[dn2_a] = str(dn2_a)
                with self.assertRaises(TypeError):
                    d[dn2_b] = str(dn2_b)

                with self.assertRaises(TypeError):
                    s.add(dn1_a)
                with self.assertRaises(TypeError):
                    s.add(dn1_b)
                with self.assertRaises(TypeError):
                    s.add(dn2_a)
                with self.assertRaises(TypeError):
                    s.add(dn2_b)
            else:
                d[dn1_a] = str(dn1_a)
                d[dn1_b] = str(dn1_b)
                d[dn2_a] = str(dn2_a)
                d[dn2_b] = str(dn2_b)

                s.add(dn1_a)
                s.add(dn1_b)
                s.add(dn2_a)
                s.add(dn2_b)

                self.assertEqual(len(d), 2)
                self.assertEqual(len(s), 2)
                self.assertEqual(sorted(d.keys()), sorted([dn1_a, dn2_a]))
                self.assertEqual(sorted(s), sorted([dn1_a, dn2_a]))

                self.assertTrue(dn1_a in d)
                self.assertTrue(dn1_b in d)
                self.assertTrue(dn2_a in d)
                self.assertTrue(dn2_b in d)
                self.assertFalse(dn3_a in d)
                self.assertFalse(dn3_b in d)

                self.assertTrue(d.has_key(dn1_a))
                self.assertTrue(d.has_key(dn1_b))
                self.assertTrue(d.has_key(dn2_a))
                self.assertTrue(d.has_key(dn2_b))
                self.assertFalse(d.has_key(dn3_a))
                self.assertFalse(d.has_key(dn3_b))

                self.assertTrue(dn1_a in s)
                self.assertTrue(dn1_b in s)
                self.assertTrue(dn2_a in s)
                self.assertTrue(dn2_b in s)
                self.assertFalse(dn3_a in s)
                self.assertFalse(dn3_b in s)

    def test_coerce(self):
        # Coerce an immutable to a mutable
        immutable_dn3 = DN(self.dn3)
        mutable_dn3 = EditableDN(immutable_dn3)
        self.assertEqual(mutable_dn3, self.dn3)
        self.assertEqual(mutable_dn3, immutable_dn3)

        # Coerce a mutable to an immutable
        mutable_dn3 = EditableDN(self.dn3)
        immutable_dn3 = DN(mutable_dn3)
        self.assertEqual(immutable_dn3, self.dn3)
        self.assertEqual(immutable_dn3, mutable_dn3)

class TestEscapes(unittest.TestCase):
    def setUp(self):
        self.privilege = 'R,W privilege'
        self.dn_str_hex_escape = 'cn=R\\2cW privilege,cn=privileges,cn=pbac,dc=idm,dc=lab,dc=bos,dc=redhat,dc=com'
        self.dn_str_backslash_escape = 'cn=R\\,W privilege,cn=privileges,cn=pbac,dc=idm,dc=lab,dc=bos,dc=redhat,dc=com'

    def test_escape(self):
        for DN_class in (DN, EditableDN):
            dn = DN_class(self.dn_str_hex_escape)
            self.assertEqual(dn['cn'], self.privilege)
            self.assertEqual(dn[0].value, self.privilege)

            dn = DN_class(self.dn_str_backslash_escape)
            self.assertEqual(dn['cn'], self.privilege)
            self.assertEqual(dn[0].value, self.privilege)

class TestInternationalization(unittest.TestCase):
    def setUp(self):
        # Hello in Arabic
        self.arabic_hello_utf8 = '\xd9\x85\xd9\x83\xd9\x8a\xd9\x84' + \
                                 '\xd8\xb9\x20\xd9\x85\xd8\xa7\xd9' + \
                                 '\x84\xd9\x91\xd8\xb3\xd9\x84\xd8\xa7'

        self.arabic_hello_unicode = self.arabic_hello_utf8.decode('utf-8')

    def test_i18n(self):
        self.assertEqual(self.arabic_hello_utf8,
                         self.arabic_hello_unicode.encode('utf-8'))

        # AVA's
        # test attr i18n
        for AVA_class in (AVA, EditableAVA):
            ava1 = AVA_class(self.arabic_hello_unicode, 'foo')
            self.assertIsInstance(ava1.attr,  unicode)
            self.assertIsInstance(ava1.value, unicode)
            self.assertEqual(ava1.attr, self.arabic_hello_unicode)
            self.assertEqual(str(ava1), self.arabic_hello_utf8+'=foo')

            ava1 = AVA_class(self.arabic_hello_utf8, 'foo')
            self.assertIsInstance(ava1.attr,  unicode)
            self.assertIsInstance(ava1.value, unicode)
            self.assertEqual(ava1.attr, self.arabic_hello_unicode)
            self.assertEqual(str(ava1), self.arabic_hello_utf8+'=foo')

            # test value i18n
            ava1 = AVA_class('cn', self.arabic_hello_unicode)
            self.assertIsInstance(ava1.attr,  unicode)
            self.assertIsInstance(ava1.value, unicode)
            self.assertEqual(ava1.value, self.arabic_hello_unicode)
            self.assertEqual(str(ava1), 'cn='+self.arabic_hello_utf8)

            ava1 = AVA_class('cn', self.arabic_hello_utf8)
            self.assertIsInstance(ava1.attr,  unicode)
            self.assertIsInstance(ava1.value, unicode)
            self.assertEqual(ava1.value, self.arabic_hello_unicode)
            self.assertEqual(str(ava1), 'cn='+self.arabic_hello_utf8)

        # RDN's
        # test attr i18n
        for RDN_class in (RDN, EditableRDN):
            rdn1 = RDN_class((self.arabic_hello_unicode, 'foo'))
            self.assertIsInstance(rdn1.attr,  unicode)
            self.assertIsInstance(rdn1.value, unicode)
            self.assertEqual(rdn1.attr, self.arabic_hello_unicode)
            self.assertEqual(str(rdn1), self.arabic_hello_utf8+'=foo')

            rdn1 = RDN_class((self.arabic_hello_utf8, 'foo'))
            self.assertIsInstance(rdn1.attr,  unicode)
            self.assertIsInstance(rdn1.value, unicode)
            self.assertEqual(rdn1.attr, self.arabic_hello_unicode)
            self.assertEqual(str(rdn1), self.arabic_hello_utf8+'=foo')

            # test value i18n
            rdn1 = RDN_class(('cn', self.arabic_hello_unicode))
            self.assertIsInstance(rdn1.attr,  unicode)
            self.assertIsInstance(rdn1.value, unicode)
            self.assertEqual(rdn1.value, self.arabic_hello_unicode)
            self.assertEqual(str(rdn1), 'cn='+self.arabic_hello_utf8)

            rdn1 = RDN_class(('cn', self.arabic_hello_utf8))
            self.assertIsInstance(rdn1.attr,  unicode)
            self.assertIsInstance(rdn1.value, unicode)
            self.assertEqual(rdn1.value, self.arabic_hello_unicode)
            self.assertEqual(str(rdn1), 'cn='+self.arabic_hello_utf8)

        # DN's
        # test attr i18n
        for DN_class in (DN, EditableDN):
            dn1 = DN_class((self.arabic_hello_unicode, 'foo'))
            self.assertIsInstance(dn1[0].attr,  unicode)
            self.assertIsInstance(dn1[0].value, unicode)
            self.assertEqual(dn1[0].attr, self.arabic_hello_unicode)
            self.assertEqual(str(dn1), self.arabic_hello_utf8+'=foo')

            dn1 = DN_class((self.arabic_hello_utf8, 'foo'))
            self.assertIsInstance(dn1[0].attr,  unicode)
            self.assertIsInstance(dn1[0].value, unicode)
            self.assertEqual(dn1[0].attr, self.arabic_hello_unicode)
            self.assertEqual(str(dn1), self.arabic_hello_utf8+'=foo')

            # test value i18n
            dn1 = DN_class(('cn', self.arabic_hello_unicode))
            self.assertIsInstance(dn1[0].attr,  unicode)
            self.assertIsInstance(dn1[0].value, unicode)
            self.assertEqual(dn1[0].value, self.arabic_hello_unicode)
            self.assertEqual(str(dn1), 'cn='+self.arabic_hello_utf8)

            dn1 = DN_class(('cn', self.arabic_hello_utf8))
            self.assertIsInstance(dn1[0].attr,  unicode)
            self.assertIsInstance(dn1[0].value, unicode)
            self.assertEqual(dn1[0].value, self.arabic_hello_unicode)
            self.assertEqual(str(dn1), 'cn='+self.arabic_hello_utf8)

if __name__ == '__main__':
    unittest.main()
