From 6f8e54338a038c3d16aa13943b5b4d74df6aba8a Mon Sep 17 00:00:00 2001
From: Violette Roche-Montane <violette.roche-montane@forgerock.com>
Date: Mon, 01 Oct 2012 06:58:23 +0000
Subject: [PATCH] CR-682 - LDIFReaderTestCase & LDIFChangeRecordReaderTestCase

---
 opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFEntryReaderTestCase.java        | 1593 +++++++++++++++++++++++++++
 opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java | 1724 ++++++++++++++++++++++++++++++
 2 files changed, 3,250 insertions(+), 67 deletions(-)

diff --git a/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java b/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java
index b083386..040a7c0 100644
--- a/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java
+++ b/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java
@@ -22,6 +22,7 @@
  *
  *
  *      Copyright 2011 ForgeRock AS
+ *      Portions copyright 2012 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldif;
@@ -30,14 +31,24 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyListOf;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
 
 import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizedIllegalArgumentException;
+import org.forgerock.opendj.ldap.AttributeDescription;
 import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.LinkedAttribute;
 import org.forgerock.opendj.ldap.Modification;
 import org.forgerock.opendj.ldap.ModificationType;
@@ -46,6 +57,8 @@
 import org.forgerock.opendj.ldap.requests.DeleteRequest;
 import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.schema.Schema;
+import org.forgerock.opendj.ldap.schema.SchemaBuilder;
 import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy;
 import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy.Policy;
 import org.testng.annotations.Test;
@@ -54,13 +67,544 @@
  * This class tests the LDIFChangeRecordReader functionality.
  */
 public final class LDIFChangeRecordReaderTestCase extends LDIFTestCase {
+
+    /**
+     * Provide a standard LDIF Change Record, valid, for tests below.
+     *
+     * @return a string containing a standard LDIF Change Record.
+     */
+    public final String[] getStandardLDIFChangeRecord() {
+
+        // @formatter:off
+        final String[] cr = {
+            "version: 1",
+            "dn: uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: add",
+            "sn: Carter",
+            "cn: Samnatha Carter",
+            "givenName: Sam",
+            "objectClass: inetOrgPerson",
+            "telephoneNumber: 555 555-5555",
+            "mail: scarter@mail.org",
+            "entryDN: uid=scarter,ou=people,dc=example,dc=org",
+            "entryUUID: ad55a34a-763f-358f-93f9-da86f9ecd9e4",
+            "modifyTimestamp: 20120903142126Z",
+            "modifiersName: cn=Internal Client,cn=Root DNs,cn=config"
+        };
+        // @formatter:on
+        return cr;
+    }
+
+    /**
+     * Test to read an LDIFChangeRecord excluding all operational attributes. In
+     * this case, force to false, all attributes must be read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAllOperationalAttributesFalse() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setExcludeAllOperationalAttributes(false);
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("entryUUID")).isTrue();
+        assertThat(addRequest.containsAttribute("entryDN")).isTrue();
+        assertThat(addRequest.containsAttribute("modifyTimestamp")).isTrue();
+        assertThat(addRequest.containsAttribute("modifiersName")).isTrue();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(10);
+    }
+
+    /**
+     * All operational attributes are excluded (true). Therefore, they musn't
+     * appear in the request.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAllOperationalAttributesTrue() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setExcludeAllOperationalAttributes(true);
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        // Operational attributes are successfully excluded:
+        assertThat(addRequest.containsAttribute("entryUUID")).isFalse();
+        assertThat(addRequest.containsAttribute("entryDN")).isFalse();
+        assertThat(addRequest.containsAttribute("modifyTimestamp")).isFalse();
+        assertThat(addRequest.containsAttribute("modifiersName")).isFalse();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        // - 4 operational
+        assertThat(addRequest.getAttributeCount()).isEqualTo(6);
+    }
+
+    /**
+     * All user attributes are excluded (false). The reader must fully return
+     * the ldif-changes. The changetype line doesn't appear.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAllUserAttributesFalse() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setExcludeAllUserAttributes(false);
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("sn")).isTrue();
+        assertThat(addRequest.containsAttribute("givenName")).isTrue();
+        assertThat(addRequest.containsAttribute("mail")).isTrue();
+        assertThat(addRequest.containsAttribute("entryDN")).isTrue();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(10);
+    }
+
+    /**
+     * All user attributes are excluded (true). The reader must return the
+     * ldif-changes without the user attributes. The changetype line doesn't
+     * appear.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAllUserAttributesTrue() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setExcludeAllUserAttributes(true);
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("sn")).isFalse();
+        assertThat(addRequest.containsAttribute("givenName")).isFalse();
+        assertThat(addRequest.containsAttribute("mail")).isFalse();
+        assertThat(addRequest.containsAttribute("entryDN")).isTrue();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(4);
+    }
+
+    /**
+     * Test to read an entry with attribute exclusions. Three attributes
+     * excluded, entry must contain the others.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testSetExcludeAttributeWithMatch() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setExcludeAttribute(AttributeDescription.valueOf("cn"));
+        reader.setExcludeAttribute(AttributeDescription.valueOf("cn"));
+        reader.setExcludeAttribute(AttributeDescription.valueOf("sn"));
+        reader.setExcludeAttribute(AttributeDescription.valueOf("entryDN"));
+
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("entryDN")).isFalse();
+        assertThat(addRequest.containsAttribute("sn")).isFalse();
+        assertThat(addRequest.containsAttribute("cn")).isFalse();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.containsAttribute("mail")).isTrue();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(7);
+    }
+
+    /**
+     * Test to read an entry with attribute exclusions. One non-existent
+     * attribute is defined. Record must be complete.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testSetExcludeAttributeWithNoMatch() throws Exception {
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setExcludeAttribute(AttributeDescription.valueOf("vip"));
+
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("entryDN")).isTrue();
+        assertThat(addRequest.containsAttribute("sn")).isTrue();
+        assertThat(addRequest.containsAttribute("cn")).isTrue();
+        assertThat(addRequest.containsAttribute("mail")).isTrue();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.getAttribute("vip")).isNull();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(10);
+    }
+
+    /**
+     * SetExcludeAttribute doesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetExcludeAttributeDoesntAllowNull() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setExcludeAttribute(null);
+    }
+
+    /**
+     * Test to read an entry with attribute including.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetIncludeAttributeWithMatch() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setIncludeAttribute(AttributeDescription.valueOf("cn"));
+        reader.setIncludeAttribute(AttributeDescription.valueOf("cn"));
+        reader.setIncludeAttribute(AttributeDescription.valueOf("sn"));
+        reader.setIncludeAttribute(AttributeDescription.valueOf("entryDN"));
+
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("entryDN")).isTrue();
+        assertThat(addRequest.containsAttribute("sn")).isTrue();
+        assertThat(addRequest.containsAttribute("cn")).isTrue();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.containsAttribute("mail")).isFalse();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(3);
+    }
+
+    /**
+     * Test to read an ldifChangeRecord with attribute including.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetIncludeAttributeWithNoMatch() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+
+        reader.setIncludeAttribute(AttributeDescription.valueOf("manager"));
+
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("entryDN")).isFalse();
+        assertThat(addRequest.containsAttribute("sn")).isFalse();
+        assertThat(addRequest.containsAttribute("cn")).isFalse();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.containsAttribute("mail")).isFalse();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(0);
+    }
+
+    /**
+     * SetIncludeAttribute doesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetIncludeAttributeDoesntAllowNull() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader("version: 1",
+                        "dn: uid=scarter,ou=People,dc=example,dc=com");
+        reader.setIncludeAttribute(null);
+    }
+
+    /**
+     * Test SetIncludeBranch method of LDIFChangeRecordReader.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetIncludeBranchWithMatch() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+        reader.setIncludeBranch(DN.valueOf("dc=example,dc=com"));
+
+        final ChangeRecord cr = reader.readChangeRecord();
+        reader.close();
+
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("entryDN")).isTrue();
+        assertThat(addRequest.containsAttribute("sn")).isTrue();
+        assertThat(addRequest.containsAttribute("cn")).isTrue();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.containsAttribute("mail")).isTrue();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(10);
+    }
+
+    /**
+     * Test SetIncludeBranch method of LDIFChangeRecordReader. The branch is not
+     * included, throw an NoSuchElementException.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NoSuchElementException.class)
+    public void testSetIncludeBranchWithNoMatch() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+        reader.setIncludeBranch(DN.valueOf("dc=example,dc=org"));
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * SetIncludeBranch doesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetIncludeBranchDoesntAllowNull() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader("version: 1",
+                        "dn: uid=scarter,ou=People,dc=example,dc=com");
+        reader.setIncludeBranch(null);
+    }
+
+    /**
+     * Test SetExcludeBranch method of LDIFChangeRecordReader.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeBranchWithMatch() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+        reader.setExcludeBranch(DN.valueOf("dc=example,dc=org"));
+        ChangeRecord cr = null;
+
+        cr = reader.readChangeRecord();
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(addRequest.containsAttribute("entryDN")).isTrue();
+        assertThat(addRequest.containsAttribute("sn")).isTrue();
+        assertThat(addRequest.containsAttribute("cn")).isTrue();
+        assertThat(addRequest.containsAttribute("changetype")).isFalse();
+        assertThat(addRequest.containsAttribute("mail")).isTrue();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(10);
+        reader.close();
+    }
+
+    /**
+     * Test SetExcludeBranch method of LDIFChangeRecordReader.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NoSuchElementException.class)
+    public void testSetExcludeBranchWithNoMatch() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader(getStandardLDIFChangeRecord());
+        reader.setExcludeBranch(DN.valueOf("dc=example,dc=com"));
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * SetExcludeBranch doesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetExcludeBranchDoesntAllowNull() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader("version: 1",
+                        "dn: uid=scarter,ou=People,dc=example,dc=com");
+        reader.setExcludeBranch(null);
+    }
+
+    /**
+     * SetSchema doesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetSchemaDoesntAllowNull() throws Exception {
+
+        final LDIFChangeRecordReader reader =
+                new LDIFChangeRecordReader("version: 1",
+                        "dn: uid=scarter,ou=People,dc=example,dc=com");
+        reader.setSchema(null);
+    }
+
+    /**
+     * LDIFChangeRecordReader setSchemaValidationPolicy. Validate the
+     * ChangeRecord depending of the selected policy. ChangeRecord is here
+     * allowed because it fills the case of validation.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetSchemaValidationPolicyDefaultAllowsEntry() throws Exception {
+
+        // @formatter:off
+        final String[] strChangeRecord = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com",
+            "changetype: add",
+            "sn: Carter",
+            "objectClass: person",
+            "objectClass: top",
+            "cn: Aaccf Amar",
+            "sn: Amar"
+        };
+        // @formatter:on
+
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(strChangeRecord);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        final ChangeRecord cr = reader.readChangeRecord();
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(cr.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(addRequest.containsAttribute("sn")).isTrue();
+        assertThat(addRequest.containsAttribute("cn")).isTrue();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(3);
+
+        reader.close();
+
+    }
+
+    /**
+     * LDIFChangeRecordReader setSchemaValidationPolicy. Validate the Change
+     * Record depending of the selected policy. ChangeRecord is here NOT allowed
+     * because it contains a uid attribute which is not allowed by the
+     * SchemaValidationPolicy.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testSetSchemaValidationPolicyDefaultRejectsEntry() throws Exception {
+        // @formatter:off
+        String[] strChangeRecord = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com",
+            "changetype: add",
+            "sn: Carter",
+            "objectClass: person",
+            "objectClass: top",
+            "cn: Aaccf Amar",
+            "sn: Amar",
+            "uid: user.0"
+        };
+        // @formatter:on
+
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(strChangeRecord);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test an LDIFRecordChange with an empty pair key. Must throw an exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testReadAddRecordWithEmptyPairKeyChangeType() throws Exception {
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "dn: dc=example,dc=com",
+            ":add" // if empty spaces, ko.
+        );
+        // @formatter:on
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFChangeRecordReader used with a wrong changetype. Must return an
+     * exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testReadAddRecordWithWrongChangeType() throws Exception {
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "dn: dc=example,dc=com",
+            "changetype: oops", // wrong
+            "objectClass: top",
+            "objectClass: domainComponent",
+            "dc: example"
+        );
+        // @formatter:on
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
     /**
      * Tests reading a valid add change record with a changetype.
      *
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testReadAddRecordWithChangeType() throws Exception {
         // @formatter:off
         LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -80,6 +624,8 @@
         assertThat(addRequest.containsAttribute("objectClass", "top", "domainComponent")).isTrue();
         assertThat(addRequest.containsAttribute("dc", "example")).isTrue();
         assertThat(addRequest.getAttributeCount()).isEqualTo(2);
+        assertThat(reader.hasNext()).isFalse();
+        reader.close();
     }
 
     /**
@@ -88,7 +634,7 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testReadAddRecordWithoutChangeType() throws Exception {
         // @formatter:off
         LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -107,6 +653,7 @@
         assertThat(addRequest.containsAttribute("objectClass", "top", "domainComponent")).isTrue();
         assertThat(addRequest.containsAttribute("dc", "example")).isTrue();
         assertThat(addRequest.getAttributeCount()).isEqualTo(2);
+        reader.close();
     }
 
     /**
@@ -115,7 +662,7 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testReadModifyRecord() throws Exception {
         // @formatter:off
         LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -209,28 +756,7 @@
         assertThat(modification.getAttribute()).isEqualTo(new LinkedAttribute("description", "1"));
 
         assertThat(changes.hasNext()).isFalse();
-    }
-
-    /**
-     * Tests reading a valid delete change record.
-     *
-     * @throws Exception
-     *             if an unexpected error occurred.
-     */
-    @Test
-    public void testReadDeleteRecord() throws Exception {
-        // @formatter:off
-        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
-            "dn: dc=example,dc=com",
-            "changetype: delete"
-        );
-        // @formatter:on
-
-        assertThat(reader.hasNext()).isTrue();
-        ChangeRecord record = reader.readChangeRecord();
-        assertThat(record).isInstanceOf(DeleteRequest.class);
-        DeleteRequest deleteRequest = (DeleteRequest) record;
-        assertThat((Object) deleteRequest.getName()).isEqualTo(DN.valueOf("dc=example,dc=com"));
+        reader.close();
     }
 
     /**
@@ -239,7 +765,7 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testReadModdnRecordWithoutNewSuperior() throws Exception {
         // @formatter:off
         LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -258,6 +784,7 @@
         assertThat((Object) modifyDNRequest.getNewRDN()).isEqualTo(RDN.valueOf("dc=eggsample"));
         assertThat(modifyDNRequest.isDeleteOldRDN()).isTrue();
         assertThat(modifyDNRequest.getNewSuperior()).isNull();
+        reader.close();
     }
 
     /**
@@ -266,7 +793,7 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testReadModdnRecordWithNewSuperior() throws Exception {
         // @formatter:off
         LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -286,6 +813,7 @@
         assertThat((Object) modifyDNRequest.getNewRDN()).isEqualTo(RDN.valueOf("dc=eggsample"));
         assertThat(modifyDNRequest.isDeleteOldRDN()).isTrue();
         assertThat((Object) modifyDNRequest.getNewSuperior()).isEqualTo(DN.valueOf("dc=org"));
+        reader.close();
     }
 
     /**
@@ -294,8 +822,9 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testRejectedRecordListenerMalformedFirstRecord() throws Exception {
+
         RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
 
         // @formatter:off
@@ -305,9 +834,9 @@
             "objectClass: top",
             "objectClass: domainComponent",
             "dc: example"
-        ).setRejectedLDIFListener(listener);
+        );
         // @formatter:on
-
+        reader.setRejectedLDIFListener(listener);
         assertThat(reader.hasNext()).isFalse();
 
         verify(listener).handleMalformedRecord(
@@ -315,6 +844,7 @@
                 eq(Arrays.asList("dn: baddn", "changetype: add", "objectClass: top",
                         "objectClass: domainComponent", "dc: example")),
                 any(LocalizableMessage.class));
+        reader.close();
     }
 
     /**
@@ -323,8 +853,9 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testRejectedRecordListenerMalformedSecondRecord() throws Exception {
+
         RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
 
         // @formatter:off
@@ -340,9 +871,9 @@
             "objectClass: top",
             "objectClass: domainComponent",
             "dc: example"
-        ).setRejectedLDIFListener(listener);
+        );
         // @formatter:on
-
+        reader.setRejectedLDIFListener(listener);
         reader.readChangeRecord(); // Skip good record.
         assertThat(reader.hasNext()).isFalse();
 
@@ -351,6 +882,7 @@
                 eq(Arrays.asList("dn: baddn", "changetype: add", "objectClass: top",
                         "objectClass: domainComponent", "dc: example")),
                 any(LocalizableMessage.class));
+        reader.close();
     }
 
     /**
@@ -359,7 +891,7 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testRejectedRecordListenerSkipsRecord() throws Exception {
         RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
 
@@ -370,9 +902,9 @@
             "objectClass: top",
             "objectClass: domainComponent",
             "dc: example"
-        ).setRejectedLDIFListener(listener).setExcludeBranch(DN.valueOf("dc=com"));
+        );
         // @formatter:on
-
+        reader.setRejectedLDIFListener(listener).setExcludeBranch(DN.valueOf("dc=com"));
         assertThat(reader.hasNext()).isFalse();
 
         verify(listener).handleSkippedRecord(
@@ -380,6 +912,7 @@
                 eq(Arrays.asList("dn: dc=example,dc=com", "changetype: add", "objectClass: top",
                         "objectClass: domainComponent", "dc: example")),
                 any(LocalizableMessage.class));
+        reader.close();
     }
 
     /**
@@ -389,7 +922,7 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testRejectedRecordListenerRejectsBadSchemaRecord() throws Exception {
         RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
 
@@ -401,7 +934,8 @@
             "objectClass: domainComponent",
             "dc: example",
             "xxx: unknown attribute"
-        ).setRejectedLDIFListener(listener)
+        );
+        reader.setRejectedLDIFListener(listener)
              .setSchemaValidationPolicy(
                  SchemaValidationPolicy.ignoreAll()
                      .checkAttributesAndObjectClasses(Policy.REJECT));
@@ -414,6 +948,7 @@
                 eq(Arrays.asList("dn: dc=example,dc=com", "changetype: add", "objectClass: top",
                         "objectClass: domainComponent", "dc: example", "xxx: unknown attribute")),
                 anyListOf(LocalizableMessage.class));
+        reader.close();
     }
 
     /**
@@ -423,7 +958,7 @@
      * @throws Exception
      *             if an unexpected error occurred.
      */
-    @Test
+    @Test()
     public void testRejectedRecordListenerWarnsBadSchemaRecord() throws Exception {
         RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
 
@@ -435,7 +970,8 @@
             "objectClass: domainComponent",
             "dc: example",
             "xxx: unknown attribute"
-        ).setRejectedLDIFListener(listener)
+        );
+        reader.setRejectedLDIFListener(listener)
              .setSchemaValidationPolicy(
                  SchemaValidationPolicy.ignoreAll()
                      .checkAttributesAndObjectClasses(Policy.WARN));
@@ -456,5 +992,1113 @@
                 eq(Arrays.asList("dn: dc=example,dc=com", "changetype: add", "objectClass: top",
                         "objectClass: domainComponent", "dc: example", "xxx: unknown attribute")),
                 anyListOf(LocalizableMessage.class));
+        reader.close();
     }
+
+    /**
+     * Read an example containing an invalid url. Must throw an exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testReadFileContainingInvalidURLThrowsError() throws Exception {
+
+        // @formatter:off
+        final  LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "version: 1",
+            "# Add a new entry",
+            "dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com",
+            "changetype: add",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Fiona Jensen",
+            "sn: Jensen",
+            "uid: fiona",
+            "telephonenumber: +1 408 555 1212",
+            "jpegphoto:< http://www.FionaJensen.www"
+        );
+        // @formatter:on
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Read a complete LDIFChangeRecord containing serie of changes.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testReadFileContainingSerieOfChanges() throws Exception {
+        // @formatter:off
+        final  LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+                "version: 1",
+                "# Add a new entry",
+                "dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com",
+                "changetype: add",
+                "objectclass: top",
+                "objectclass: person",
+                "objectclass: organizationalPerson",
+                "cn: Fiona Jensen",
+                "sn: Jensen",
+                "uid: fiona",
+                "telephonenumber: +1 408 555 1212",
+                "jpegphoto:< http://www.forgerock.com/sites/default/files/forgerock_logo.png",
+                "",
+                "# Delete an existing entry",
+                "dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com",
+                "changetype: delete",
+                "",
+                "# Modify an entry's relative distinguished name",
+                "dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com",
+                "changetype: modrdn",
+                "newrdn: cn=Paula Jensen",
+                "deleteoldrdn: 1",
+                "",
+                "# Rename an entry and move all of its children to a new location in",
+                "# the directory tree (only implemented by LDAPv3 servers).",
+                "dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com",
+                "changetype: modrdn",
+                "newrdn: ou=Product Development Accountants",
+                "deleteoldrdn: 0",
+                "newsuperior: ou=Accounting, dc=airius, dc=com",
+                ""
+        );
+        // @formatter:on
+        assertThat(reader.hasNext()).isTrue();
+        // 1st record
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(AddRequest.class);
+        AddRequest addReq = (AddRequest) record;
+        assertThat((Object) addReq.getName()).isEqualTo(
+                DN.valueOf("cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com"));
+        // 2nd record
+        record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(DeleteRequest.class);
+        DeleteRequest delReq = (DeleteRequest) record;
+        assertThat((Object) delReq.getName()).isEqualTo(
+                DN.valueOf("cn=Robert Jensen, ou=Marketing, dc=airius, dc=com"));
+        assertThat(reader.hasNext()).isTrue();
+        // 3rd record
+        record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(ModifyDNRequest.class);
+        ModifyDNRequest modDNReq = (ModifyDNRequest) record;
+
+        assertThat((Object) modDNReq.getNewRDN()).isEqualTo(RDN.valueOf("cn=Paula Jensen"));
+        assertThat((Object) modDNReq.getName()).isEqualTo(
+                DN.valueOf("cn=Paul Jensen, ou=Product Development, dc=airius, dc=com"));
+        assertThat(reader.hasNext()).isTrue();
+        // 4th record
+        record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(ModifyDNRequest.class);
+        modDNReq = (ModifyDNRequest) record;
+        assertThat((Object) modDNReq.getName()).isEqualTo(
+                DN.valueOf("ou=PD Accountants, ou=Product Development, dc=airius, dc=com"));
+        assertThat(reader.hasNext()).isFalse();
+
+        reader.close();
+    }
+
+    /**
+     * Test to read an entry containing a control. Not Yet implemented. TODO
+     * OPENDJ-185 Add support for controls in SDK LDIFReader.
+     *
+     * @throws Exception
+     */
+    @Test(enabled = false)
+    public void testLDIFCRRParseAddChangeRecordEntryWithAControl() throws Exception {
+        // @formatter:off
+        final  LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+                    "# Delete an entry. The operation will attach the LDAPv3",
+                    "# Tree Delete Control defined in [9]. The criticality",
+                    "# field is \"true\" and the controlValue field is",
+                    "# absent, as required by [9].",
+                    "dn: ou=Product Development, dc=airius, dc=com",
+                    "control: 1.2.840.113556.1.4.805 true",
+                    "changetype: delete"
+        );
+        // @formatter:on
+
+        assertThat(reader.hasNext()).isTrue();
+
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+        // Read the entry
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(AddRequest.class);
+        AddRequest request = (AddRequest) record;
+        assertThat((Object) request.getName()).isEqualTo(
+                DN.valueOf("ou=Product Development, dc=airius, dc=com"));
+        assertThat(request.getControls()).isNotEmpty();
+        reader.close();
+    }
+
+    /**
+     * Test an add request malformed, changetype is erroneous (wrongchangetype)
+     * Must throw an exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseAddChangeRecordEntryLastLDIFLineIsNull() throws Exception {
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+                "dn: uid=scarter,ou=People,dc=example,dc=com",
+                "wrongchangetype: add", // wrong
+                "uid:Carter"
+        );
+        // @formatter:on
+
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Tests reading a valid delete change record.
+     *
+     * @throws Exception
+     *             if an unexpected error occurred.
+     */
+    @Test()
+    public void testLDIFCRRparseDeleteChangeRecordEntry() throws Exception {
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "dn: dc=example,dc=com",
+            "changetype: delete"
+        );
+        // @formatter:on
+
+        assertThat(reader.hasNext()).isTrue();
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(DeleteRequest.class);
+        DeleteRequest deleteRequest = (DeleteRequest) record;
+        assertThat((Object) deleteRequest.getName()).isEqualTo(DN.valueOf("dc=example,dc=com"));
+        reader.close();
+    }
+
+    /**
+     * Testing a valid LDIFChangeRecord with the delete type. The LDIF is
+     * containing additional lines after the 'changetype' when none were
+     * expected. Exception must be throw.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseDeleteChangeRecordEntryMalformedDelete() throws Exception {
+
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+                "# Delete an existing entry",
+                "dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com",
+                "changetype: delete",
+                "-",
+                "add: telephonenumber",
+                "telephonenumber: 555-4321"
+        );
+        // @formatter:on
+        reader.readChangeRecord();
+    }
+
+    /**
+     * Read an LDIFChangeRecord for deleting selected values of a multi-valued
+     * attribute.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFCRRParseModifyChangeRecordEntryDeleteMultipleValuableAttributes()
+            throws Exception {
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+                "# Add new entry containing multiple attributes",
+                "dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com",
+                "changetype: modify",
+                "delete: telephonenumber",
+                "telephonenumber: +1 408 555 1212",
+                "telephonenumber: +1 408 555 1213",
+                "telephonenumber: +1 408 555 1214"
+        );
+        // @formatter:on
+        assertThat(reader.hasNext()).isTrue();
+
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(ModifyRequest.class);
+        ModifyRequest req = (ModifyRequest) record;
+        assertThat((Object) req.getName()).isEqualTo(
+                DN.valueOf("cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com"));
+        assertThat(reader.hasNext()).isFalse();
+    }
+
+    /**
+     * Read an LDIFChangeRecord for deleting selected values of a multi-valued
+     * attribute. The 2nd attribute is malformed : Schema validation failure.
+     * ERR_LDIF_MALFORMED_ATTRIBUTE_NAME : The provided value is not a valid
+     * telephone number because it is empty or null
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyChangeRecordEntryDeleteMultipleValuableAttributesMalformedLDIF()
+            throws Exception {
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+                "# Add new entry containing multiple attributes",
+                "dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com",
+                "changetype: modify",
+                "delete: telephonenumber",
+                "telephonenumber: +1 408 555 1212",
+                "telephonenumber:", // wrong!
+                "telephonenumber: +1 408 555 1214",
+                "-"
+        );
+        // @formatter:on
+
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * BER encoding is required for this LDIFChangeRecord. After adding the user
+     * certificate to the core schema, LDIFCRR is correctly read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFCRRParseModifyChangeRecordBEREncodingRequired() throws Exception {
+        // @formatter:off
+        String validcert1 = // a valid certificate but wrong can be used => no errors
+                "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV"
+                + "BAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRpb25lMRwwGgYDVQQLExNQcm9kdWN0IERl"
+                + "dmVsb3BtZW50MRQwEgYDVQQDEwtCYWJzIEplbnNlbjAeFw0xMjA1MDIxNjM0MzVa"
+                + "Fw0xMjEyMjExNjM0MzVaMFYxCzAJBgNVBAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRp"
+                + "b25lMRwwGgYDVQQLExNQcm9kdWN0IERldmVsb3BtZW50MRQwEgYDVQQDEwtCYWJz"
+                + "IEplbnNlbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApysa0c9qc8FB8gIJ"
+                + "8zAb1pbJ4HzC7iRlVGhRJjFORkGhyvU4P5o2wL0iz/uko6rL9/pFhIlIMbwbV8sm"
+                + "mKeNUPitwiKOjoFDmtimcZ4bx5UTAYLbbHMpEdwSpMC5iF2UioM7qdiwpAfZBd6Z"
+                + "69vqNxuUJ6tP+hxtr/aSgMH2i8ECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB"
+                + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE"
+                + "FLlZD3aKDa8jdhzoByOFMAJDs2osMB8GA1UdIwQYMBaAFLlZD3aKDa8jdhzoByOF"
+                + "MAJDs2osMA0GCSqGSIb3DQEBBQUAA4GBAE5vccY8Ydd7by2bbwiDKgQqVyoKrkUg"
+                + "6CD0WRmc2pBeYX2z94/PWO5L3Fx+eIZh2wTxScF+FdRWJzLbUaBuClrxuy0Y5ifj"
+                + "axuJ8LFNbZtsp1ldW3i84+F5+SYT+xI67ZcoAtwx/VFVI9s5I/Gkmu9f9nxjPpK7"
+                + "1AIUXiE3Qcck";
+
+        final String[] strChangeRecord = {
+            "version: 1",
+            "dn:uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            "add: userCertificate;binary", // with or without the binary its working
+            String.format("userCertificate;binary::%s", validcert1)
+        };
+        // @formatter:on
+
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(strChangeRecord);
+
+        final SchemaBuilder scBuild = new SchemaBuilder();
+        // Adding the new schema containing the userCertificate
+        scBuild.addObjectClass("( 2.5.6.15 NAME 'strongAuthenticationUser"
+                + "' SUP top AUXILIARY MUST userCertificate )", false);
+        scBuild.addAttributeType(
+                "( 2.5.4.36 NAME 'userCertificate' SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )", false);
+        // Adding to default core schema
+        scBuild.addSchema(Schema.getCoreSchema(), false);
+        Schema schema = scBuild.toSchema();
+        reader.setSchema(schema);
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        ModifyRequest modifyRequest = (ModifyRequest) reader.readChangeRecord();
+        assertThat(modifyRequest.getName().toString()).isEqualTo(
+                "uid=scarter,ou=People,dc=example,dc=com");
+        assertThat(modifyRequest.getModifications().get(0).getModificationType().toString())
+                .isEqualTo("add");
+        assertThat(modifyRequest.getModifications().get(0).getAttribute().firstValueAsString())
+                .contains("OpenSSL Generated Certificate");
+        reader.close();
+    }
+
+    /**
+     * LDIFChangeREcord reader try to add an unexpected binary option to the sn.
+     * sn is a included in the core schema. Must throw an exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyChangeRecordBEREncodingNotRequired() throws Exception {
+        // @formatter:off
+        final String[] strChangeRecord = {
+            "version: 1",
+            "dn:uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            "add: sn;binary",
+            "sn;binary:: 5bCP56yg5Y6f"
+        };
+        // @formatter:on
+
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(strChangeRecord);
+
+        Schema schema = Schema.getCoreSchema();
+        reader.setSchema(schema);
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test a 'modify' change record, respecting the default schema.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFCRRParseModifyChangeRecordEntryReplaceOk() throws Exception {
+
+        // @formatter:off
+        final String[] strChangeRecord = {
+            "version: 1",
+            "dn: uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            "replace: uid",
+            "uid: Samantha Carter"
+        };
+        // @formatter:on
+
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(strChangeRecord);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(ModifyRequest.class);
+
+        ModifyRequest modifyRequest = (ModifyRequest) record;
+        assertThat(modifyRequest.getName().toString()).isEqualTo(
+                "uid=scarter,ou=People,dc=example,dc=com");
+        assertThat(modifyRequest.getModifications().get(0).getModificationType().toString())
+                .isEqualTo("replace");
+        assertThat(modifyRequest.getModifications().get(0).getAttribute().firstValueAsString())
+                .isEqualTo("Samantha Carter");
+        reader.close();
+    }
+
+    /**
+     * Test a 'modify' change record, without respecting the default schema. The
+     * 'badAttribute' is not recognized by the schema. Must throw an exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyChangeRecordEntryReplaceKOPolicyReject() throws Exception {
+
+        // @formatter:off
+        final String[] strChangeRecord = {
+            "version: 1",
+            "dn: uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            "replace: badAttribute",
+            "badAttribute: scarter"
+        };
+        // @formatter:on
+
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(strChangeRecord);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Change Record throw an exception because the added attribute is not valid
+     * ('badAttribute') relative to the default schema. Here, we use a
+     * Policy.warn instead of a Policy.REJECT (default)
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyChangeRecordEntryReplaceKOPolicyWarn() throws Exception {
+
+        // @formatter:off
+        final String[] strChangeRecord = {
+            "version: 1",
+            "dn: uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            "replace: badAttribute",
+            "badAttribute: scarter"
+        };
+        // @formatter:on
+
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(strChangeRecord);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy()
+                .checkAttributesAndObjectClasses(Policy.WARN));
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Change Record throw an exception because the space added just before uid
+     * provokes an LocalizedIllegalArgumentException.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyChangeRecordEntryReplaceLocalizedIllegalArgumentException()
+            throws Exception {
+
+        // @formatter:off
+        final String[] strChangeRecord = {
+            "version: 1",
+            "dn: uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            "replace: uid",
+            " uid:Samantha Carter" // the space before provokes an LocalizedIllegalArgumentException
+        };
+        // @formatter:on
+
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(strChangeRecord);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+
+    }
+
+    /**
+     * Read a malformed Change Record : changetype is wrong.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testLDIFCRRParseModifyChangeRecordEntryWithWrongChangetype() {
+        // @formatter:off
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord(
+            "version: 1",
+            "dn: uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            "oops:uidNumber" // wrong
+        );
+        // @formatter:on
+    }
+
+    /**
+     * Read a malformed Change Record. 'pair.key' is null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testLDIFCRRParseModifyChangeRecordEntryWithNullPairKey() {
+        // @formatter:off
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord(
+            "version: 1",
+            "dn: uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            ":uidNumber" // wrong
+        );
+        // @formatter:on
+    }
+
+    /**
+     * Read a well formed Change Record.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFCRRParseModifyChangeRecordEntryIncrement() throws Exception {
+        // @formatter:off
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord(
+            "version: 1",
+            "dn: uid=scarter,ou=People,dc=example,dc=com",
+            "changetype: modify",
+            "increment:uidNumber",
+            "uidNumber: 1"
+        );
+        // @formatter:on
+
+        assertThat(cr).isInstanceOf(ModifyRequest.class);
+        ModifyRequest modifyRequest = (ModifyRequest) cr;
+
+        assertThat(modifyRequest.getName().toString()).isEqualTo(
+                "uid=scarter,ou=People,dc=example,dc=com");
+
+        assertThat(modifyRequest.getModifications().get(0).getModificationType().toString())
+                .isEqualTo("increment");
+    }
+
+    /**
+     * Read an LDIF Record changes. Trying to modify DN using base64 newrdn.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFCRRparseModifyDNChangeRecordEntryRecordBase64NewRDN() throws Exception {
+
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+                "dn::ZGM9cGVvcGxlLGRjPWV4YW1wbGUsZGM9b3Jn",
+                "changetype: modrdn",
+                "newrdn::ZGM9cGVvcGxlLGRjPWV4YW1wbGUsZGM9Y29t",
+                "deleteoldrdn: 1"
+        );
+        // @formatter:on
+
+        assertThat(reader.hasNext()).isTrue();
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(ModifyDNRequest.class);
+        ModifyDNRequest modifyDNRequest = (ModifyDNRequest) record;
+        assertThat((Object) modifyDNRequest.getName()).isEqualTo(
+                DN.valueOf("dc=people,dc=example,dc=org"));
+        assertThat((Object) modifyDNRequest.getNewRDN()).isEqualTo(
+                RDN.valueOf("dc=people,dc=example,dc=com"));
+        assertThat(modifyDNRequest.isDeleteOldRDN()).isTrue();
+        reader.close();
+    }
+
+    /**
+     * Modifying a DN and delete the old one.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFCRRParseModifyDNChangeRecordEntry() throws Exception {
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "version: 1",
+            "dn: cn=scarter,dc=example,dc=com",
+            "changetype: modrdn",
+            "newrdn: cn=Susan Jacobs",
+            "deleteoldrdn: 1"
+        );
+        // @formatter:on
+
+        assertThat(reader.hasNext()).isTrue();
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(ModifyDNRequest.class);
+        ModifyDNRequest modifyDNRequest = (ModifyDNRequest) record;
+        assertThat((Object) modifyDNRequest.getName()).isEqualTo(
+                DN.valueOf("cn=scarter,dc=example,dc=com"));
+        assertThat((Object) modifyDNRequest.getNewRDN()).isEqualTo(RDN.valueOf("cn=Susan Jacobs"));
+        assertThat(modifyDNRequest.isDeleteOldRDN()).isTrue();
+        assertThat((Object) modifyDNRequest.getNewSuperior()).isEqualTo(null);
+        reader.close();
+    }
+
+    /**
+     * Try to change the dn, but the new rdn is missing. Unable to parse LDIF
+     * modify DN record starting at line 1 with distinguished name
+     * "cn=scarter,dc=example,dc=com" because there was no new RDN.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyDNChangeRecordEntryMalformedMissedNewRDN() throws Exception {
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "version: 1",
+            "dn: cn=scarter,dc=example,dc=com",
+            "changetype: modrdn"
+        );
+        // @formatter:on
+
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFChangeRecord.parseModifyDNChangeRecordEntry and try to add an empty
+     * new rdn throw an error.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyDNChangeRecordEntryKeyMalformedEmptyNewRDN() throws Exception {
+
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "version: 1",
+            "dn: cn=scarter,dc=example,dc=com",
+            "changetype: modrdn",
+            "newrdn:",
+            "deleteoldrdn: 1"
+        );
+        // @formatter:on
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFChangeRecord.parseModifyDNChangeRecordEntry and try to add a
+     * malformed rdn throw an error.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyDNChangeRecordEntryKeyValueMalformedRDN() throws Exception {
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "version: 1",
+            "dn: cn=scarter,dc=example,dc=com",
+            "changetype: modrdn",
+            "newrdn:oops", // wrong
+            "deleteoldrdn: 1"
+        );
+        // @formatter:on
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFChangeRecord.parseModifyDNChangeRecordEntry and try to add a
+     * malformed rdn throw an error. (deleteoldrdn value is wrong).
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyDNChangeRecordEntryKeyValueMalformedDeleteOldRDN()
+            throws Exception {
+
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "version: 1",
+            "dn: cn=scarter,dc=example,dc=com",
+            "changetype: modrdn",
+            "newrdn:cn=Susan Jacobs",
+            "deleteoldrdn: cn=scarter,dc=example,dc=com" // wrong
+        );
+        // @formatter:on
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFChangeRecord.parseModifyDNChangeRecordEntry and try to add a
+     * malformed rdn throw an error. (pair.key != deleteoldrdn)
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyDNChangeRecordEntryKeyValueMalformedDeleteOldRDN2()
+            throws Exception {
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "version: 1",
+            "dn: cn=scarter,dc=example,dc=com",
+            "changetype: modrdn",
+            "newrdn:cn=Susan Jacobs",
+            "deleteold: 1" // wrong
+        );
+        // @formatter:on
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Try to change the DN but deleteoldrdn is missing. Must throw an exception
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRParseModifyDNChangeRecordEntryKeyValueMalformedDeleteOldRDN3()
+            throws Exception {
+
+        // @formatter:off
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "version: 1",
+            "dn: cn=scarter,dc=example,dc=com",
+            "changetype: modrdn",
+            "newrdn:cn=Susan Jacobs"
+            // missing deleteoldrn: 1/0||true/false||yes/no
+        );
+        // @formatter:on
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFCRR delete old rdn and add a new superior to the new one.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFCRRparseModifyRecordEntryDeleteOldRDNFalse() throws Exception {
+
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "dn: cn=scarter,ou=People,dc=example,dc=com",
+            "changeType: modrdn",
+            "newrdn: cn=Susan Jacobs",
+            "deleteOldRdn: 0",
+            "newSuperior:ou=Manager,dc=example,dc=org"
+        );
+        // @formatter:on
+        assertThat(reader.hasNext()).isTrue();
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(ModifyDNRequest.class);
+        ModifyDNRequest modifyDNRequest = (ModifyDNRequest) record;
+
+        assertThat((Object) modifyDNRequest.getName()).isEqualTo(
+                DN.valueOf("cn=scarter,ou=People,dc=example,dc=com"));
+        assertThat((Object) modifyDNRequest.getNewRDN()).isEqualTo(RDN.valueOf("cn=Susan Jacobs"));
+        assertThat(modifyDNRequest.isDeleteOldRDN()).isFalse();
+        assertThat((Object) modifyDNRequest.getNewSuperior().toString()).isEqualTo(
+                "ou=Manager,dc=example,dc=org");
+        reader.close();
+    }
+
+    /**
+     * LDIFCRR delete old rdn and add a new superior.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFCRRparseModifyRecordEntryNewSuperior() throws Exception {
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "dn: cn=scarter,ou=People,dc=example,dc=com",
+            "changeType: modrdn",
+            "newrdn: cn=Susan Jacobs",
+            "deleteOldRdn: 1",
+            "newSuperior:ou=Manager,dc=example,dc=org"
+        );
+        // @formatter:on
+        assertThat(reader.hasNext()).isTrue();
+        ChangeRecord record = reader.readChangeRecord();
+        assertThat(record).isInstanceOf(ModifyDNRequest.class);
+        ModifyDNRequest modifyDNRequest = (ModifyDNRequest) record;
+
+        assertThat((Object) modifyDNRequest.getName()).isEqualTo(
+                DN.valueOf("cn=scarter,ou=People,dc=example,dc=com"));
+        assertThat((Object) modifyDNRequest.getNewRDN()).isEqualTo(RDN.valueOf("cn=Susan Jacobs"));
+        assertThat(modifyDNRequest.isDeleteOldRDN()).isTrue();
+        assertThat((Object) modifyDNRequest.getNewSuperior().toString()).isEqualTo(
+                "ou=Manager,dc=example,dc=org");
+        reader.close();
+    }
+
+    /**
+     * LDIFCRR delete old rdn and add a new superior willingly malformed. Syntax
+     * is wrong "newSuperior:". Must throw an exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRparseModifyRecordEntryNewSuperiorMalformed() throws Exception {
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "dn: cn=scarter,ou=People,dc=example,dc=com",
+            "changeType: modrdn",
+            "newrdn: cn=Susan Jacobs",
+            "deleteOldRdn: 1",
+            "newSuperior:" // wrong
+        );
+        // @formatter:on
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFCRR delete old rdn and add a new superior willingly malformed. Syntax
+     * is wrong "newSuperior: Susan Jacobs". Must throw an exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFCRRparseModifyRecordEntryNewSuperiorMalformed2() throws Exception {
+
+        // @formatter:off
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
+            "dn: cn=scarter,ou=People,dc=example,dc=com",
+            "changeType: modrdn",
+            "newrdn: cn=Susan Jacobs",
+            "deleteOldRdn: 1",
+            "newSuperior: Susan Jacobs" // wrong
+        );
+        // @formatter:on
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Mock an inputstream for verifying LDIFChangeRecordReader close().
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = IOException.class)
+    public void testChangeRecordReaderClosesAfterReading() throws Exception {
+
+        final FileInputStream mockIn = mock(FileInputStream.class);
+        final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(mockIn);
+
+        doThrow(new IOException()).when(mockIn).read();
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+            verify(mockIn, times(1)).close();
+        }
+    }
+
+    /**
+     * Read an ldif-changes using a List<String>.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testChangeRecordReaderUseListConstructor() throws Exception {
+        // @formatter:off
+        List<String> cr = Arrays.asList(
+            "dn: dc=example,dc=com",
+            "changetype: add",
+            "objectClass: top",
+            "objectClass: domainComponent",
+            "dc: example"
+        );
+        // @formatter:on
+
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(cr);
+        ChangeRecord rc = reader.readChangeRecord();
+        assertThat(rc).isNotNull();
+        assertThat(rc.getName().toString()).isEqualTo("dc=example,dc=com");
+        reader.close();
+    }
+
+    /**
+     * Try to read an LDIFChangeREcord without changetype. Must throw an
+     * exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testChangeRecordReaderHasNoChange() throws Exception {
+
+        // @formatter:off
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord(
+            "version: 1",
+            "# Add a new entry without changes !",
+            "dn: dc=example,dc=com"
+        );
+        // @formatter:on
+    }
+
+    /**
+     * Try to read a null ldif-changes using a List<String>. Exception expected.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NoSuchElementException.class)
+    public void testChangeRecordReaderDoesntAllowNull() throws Exception {
+        List<String> cr = Arrays.asList();
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(cr);
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Try to read a null ldif-changes using an empty String. Exception
+     * expected.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NoSuchElementException.class)
+    public void testChangeRecordReaderLDIFLineDoesntAllowNull() throws Exception {
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(new String());
+        try {
+            reader.readChangeRecord();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFChangeRecordReader cause NullPointerException when InputStream is
+     * null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testChangeRecordReaderInpuStreamDoesntAllowNull() throws Exception {
+        final InputStream is = null;
+        @SuppressWarnings({ "unused", "resource" })
+        LDIFChangeRecordReader reader = new LDIFChangeRecordReader(is);
+    }
+
+    /**
+     * LDIFChangeRecordReader cause NullPointerException when
+     * valueOfLDIFChangeRecord is null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testValueOfLDIFChangeRecordDoesntAllowNull() throws Exception {
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord("");
+    }
+
+    /**
+     * valueOfLDIFChangeRecord cause an exception due to the presence of
+     * multiple change record.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testValueOfLDIFChangeRecordDoesntAllowMultipleChangeRecords() throws Exception {
+        // @formatter:off
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord(
+            "version: 1",
+            "# Add a new entry",
+            "dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com",
+            "changetype: add",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Fiona Jensen",
+            "sn: Jensen",
+            "uid: fiona",
+            "telephonenumber: +1 408 555 1212",
+            "jpegphotojpegphoto:< http://www.forgerock.com/sites/default/files/forgerock_logo.png",
+            "",
+            "# Delete an existing entry",
+            "dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com",
+            "changetype: delete"
+        );
+        // @formatter:on
+    }
+
+    /**
+     * valueOfLDIFChangeRecord cause an exception due to badly formed ldif. In
+     * this case, DN is missing.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testValueOfLDIFChangeRecordMalformedLDIFDNIsMissing() throws Exception {
+        // @formatter:off
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord(
+            "version: 1",
+            "# Add a new entry",
+            "changetype: add",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Fiona Jensen",
+            "sn: Jensen",
+            "uid: fiona",
+            "telephonenumber: +1 408 555 1212"
+        );
+        // @formatter:on
+        // DN is missing above.
+    }
+
+    /**
+     * Try to read a malformed LDIF : The provided LDIF content did not contain
+     * any LDIF change records. Coverage on AbstractLDIFReader -
+     * readLDIFRecordDN.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testValueOfLDIFChangeRecordMalformedLDIFContainingOnlyVersion() throws Exception {
+
+        // @formatter:off
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord(
+                "version: 1"
+        );
+        // @formatter:on
+    }
+
+    /**
+     * Try to read a malformed LDIF : Unable to parse LDIF entry starting at
+     * line 1 because the line ":wrong" does not include an attribute name.
+     * Coverage on AbstractLDIFReader - readLDIFRecordDN.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testValueOfLDIFChangeRecordMalformedLDIFContainingVersionAndWrongLine()
+            throws Exception {
+
+        // @formatter:off
+        final ChangeRecord cr = LDIFChangeRecordReader.valueOfLDIFChangeRecord(
+                "version: 1",
+                ":wrong"
+        );
+        // @formatter:on
+    }
+
+    /**
+     * Try to read a standard Change Record LDIF.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testValueOfLDIFChangeRecordStandardLDIF() throws Exception {
+
+        // @formatter:off
+        final ChangeRecord cr =
+                LDIFChangeRecordReader.valueOfLDIFChangeRecord(getStandardLDIFChangeRecord());
+        // @formatter:on
+
+        AddRequest addRequest = (AddRequest) cr;
+        assertThat(cr.getName().toString()).isEqualTo("uid=scarter,ou=People,dc=example,dc=com");
+        assertThat(addRequest.containsAttribute("sn")).isTrue();
+        assertThat(addRequest.containsAttribute("cn")).isTrue();
+        assertThat(addRequest.getAttributeCount()).isEqualTo(10);
+    }
+
 }
diff --git a/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFEntryReaderTestCase.java b/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFEntryReaderTestCase.java
index c739fb4..ae1bb9e 100644
--- a/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFEntryReaderTestCase.java
+++ b/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFEntryReaderTestCase.java
@@ -22,6 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2012 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldif;
@@ -29,26 +30,873 @@
 import static org.testng.Assert.assertNotNull;
 
 import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
 import java.util.NoSuchElementException;
 
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizedIllegalArgumentException;
+import org.forgerock.opendj.ldap.AttributeDescription;
 import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
+import org.forgerock.opendj.ldap.LinkedHashMapEntry;
+import org.forgerock.opendj.ldap.Matcher;
 import org.forgerock.opendj.ldap.TestCaseUtils;
+import org.forgerock.opendj.ldap.schema.Schema;
+import org.forgerock.opendj.ldap.schema.SchemaBuilder;
+import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy;
+import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy.Policy;
 import org.testng.Assert;
 import org.testng.annotations.Test;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
 
 /**
  * This class tests the LDIFEntryReader functionality.
  */
 public final class LDIFEntryReaderTestCase extends LDIFTestCase {
     /**
+     * Provide a standard entry for the tests below.
+     *
+     * @return well formed LDIF entry
+     */
+    public final String[] getStandardEntry() {
+        // @formatter:off
+        final String[] entry = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com",
+            "objectClass: person",
+            "objectClass: inetorgperson",
+            "objectClass: organizationalperson",
+            "objectClass: top",
+            "postalAddress: Aaccf Amar$01251 Chestnut Street$Panama City, DE  50369",
+            "postalCode: 50369",
+            "uid: user.0",
+            "description: This is the description for Aaccf Amar.",
+            "userPassword: {SSHA}hpbT8dLi8xgYy2kl4aP6QKGzsFdhESWpPmDTEw==",
+            "employeeNumber: 0",
+            "initials: ASA",
+            "givenName: Aaccf",
+            "pager: +1 779 041 6341",
+            "mobile: +1 010 154 3228",
+            "cn: Aaccf Amar",
+            "telephoneNumber: +1 685 622 6202",
+            "sn: Amar",
+            "street: 01251 Chestnut Street",
+            "homePhone: +1 225 216 5900",
+            "mail: user.0@maildomain.net",
+            "l: Panama City", "st: DE",
+            "pwdChangedTime: 20120903142126.219Z",
+            "entryDN: uid=user.0,ou=people,dc=example,dc=org",
+            "entryUUID: ad55a34a-763f-358f-93f9-da86f9ecd9e4",
+            "modifyTimestamp: 20120903142126Z",
+            "modifiersName: cn=Internal Client,cn=Root DNs,cn=config"
+        };
+        // @formatter:on
+        return entry;
+    }
+
+    /**
+     * Number of attributes of the standard entry.
+     */
+    public final int nbStandardEntryAttributes = new LinkedHashMapEntry(getStandardEntry())
+            .getAttributeCount();
+
+    /**
+     * Test SetExcludeBranch method of LDIFEntryReader. Excluding the
+     * "dc=example,dc=com" Entry is not read and function return a
+     * NoSuchElementException exception.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NoSuchElementException.class)
+    public void testSetExcludeBranchWithNoMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setExcludeBranch(DN.valueOf("dc=example,dc=com"));
+
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test SetExcludeBranch method of LDIFEntryReader. Excluding the
+     * "dc=example,dc=org", which is not in the standard ldif entry. Entry must
+     * be fully read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeBranchWithMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setExcludeBranch(DN.valueOf("dc=example,dc=org"));
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+    }
+
+    /**
+     * Test the setExcludeBranch with a null parameter.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetExcludeBranchDoesntAllowNull() throws Exception {
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setExcludeBranch(null);
+    }
+
+    /**
+     * Test to read an entry excluding user attributes. Default case - all the
+     * lines must be read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAllUserAttributesFalse() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setExcludeAllUserAttributes(false);
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+        assertThat(entry.getAttribute("entryDN")).isNotNull();
+        assertThat(entry.getAttribute("description")).isNotNull();
+    }
+
+    /**
+     * Test to read an entry excluding user attributes Only the operational
+     * attributes must be read (entryDN, entryUUID, modifyTimestamp,
+     * modifiersName...) (e.g : 4 in the standard entry)
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAllUserAttributesTrue() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+
+        reader.setExcludeAllUserAttributes(true);
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getAttribute("dn")).isNull();
+        assertThat(entry.getAttribute("sn")).isNull();
+        assertThat(entry.getAttribute("uid")).isNull();
+        assertThat(entry.getAttribute("description")).isNull();
+
+        assertThat(entry.getAttribute("entryDN")).isNotEmpty();
+        assertThat(entry.getAttribute("entryUUID")).isNotEmpty();
+        assertThat(entry.getAttribute("modifyTimestamp")).isNotNull();
+        assertThat(entry.getAttributeCount()).isEqualTo(4);
+    }
+
+    /**
+     * Test to read an entry with attribute exclusions. In this test, the
+     * attribute description 'vip' doesn't exist... the entry must be fully
+     * read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAttributeWithNoMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setExcludeAttribute(AttributeDescription.valueOf("vip"));
+
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+        // no attribute 'vip'
+        assertThat(entry.getAttribute("vip")).isNull();
+    }
+
+    /**
+     * Test to read an entry with attribute exclusions. Three attributes
+     * excluded, entry must contain the others.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAttributeWithMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+
+        reader.setExcludeAttribute(AttributeDescription.valueOf("cn"));
+        reader.setExcludeAttribute(AttributeDescription.valueOf("cn"));
+        reader.setExcludeAttribute(AttributeDescription.valueOf("sn"));
+        reader.setExcludeAttribute(AttributeDescription.valueOf("entryDN"));
+
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getAttribute("entryDN")).isNull();
+        assertThat(entry.getAttribute("sn")).isNull();
+        assertThat(entry.getAttribute("cn")).isNull();
+
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes - 3);
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+    }
+
+    /**
+     * setExcludeAttributeDoesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetExcludeAttributeDoesntAllowNull() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setExcludeAttribute(null);
+    }
+
+    /**
+     * Test to read an entry excluding all operational attributes
+     * setExcludeAllOperationalAttributes to false (default case) All attributes
+     * must be read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAllOperationalAttributesFalse() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+
+        reader.setExcludeAllOperationalAttributes(false);
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+        assertThat(entry.getAttribute("entryDN")).isNotNull();
+        assertThat(entry.getAttribute("entryUUID")).isNotNull();
+        assertThat(entry.getAttribute("modifyTimestamp")).isNotNull();
+    }
+
+    /**
+     * Test to read an entry excluding all operational attributes
+     * setExcludeAllOperationalAttributes is forced to true
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeAllOperationalAttributesTrue() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+
+        reader.setExcludeAllOperationalAttributes(true);
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getAttribute("entryDN")).isNull();
+        assertThat(entry.getAttribute("entryUUID")).isNull();
+        assertThat(entry.getAttribute("modifyTimestamp")).isNull();
+
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isLessThan(nbStandardEntryAttributes);
+    }
+
+    /**
+     * Test SetExcludeFilter method of LDIFEntryReader. Throws a
+     * NullPointerException if the excludeFilter is null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testsetExcludeFilterDoesntAllowNull() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+
+        reader.setExcludeFilter(null);
+        reader.close();
+    }
+
+    /**
+     * Test testSetExcludeFilter method of LDIFEntryReader. StandardEntry has an
+     * objectclass : person, not vip. The filter must exclude all entries with
+     * an objectclass = vip. In this case, entry must be fully read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetExcludeFilterWithMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        final Filter filter = Filter.equality("objectclass", "vip");
+        final Matcher excludeFilter = filter.matcher();
+
+        reader.setExcludeFilter(excludeFilter);
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttribute("objectclass").toString()).isNotEqualTo("vip");
+    }
+
+    /**
+     * Test testSetExcludeFilter method of LDIFEntryReader. StandardEntry has an
+     * objectclass : person. The filter must exclude all entries with an
+     * objectclass = person. Entry musn't be read.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NoSuchElementException.class)
+    public void testSetExcludeFilterWithNoMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        final Filter filter = Filter.equality("objectclass", "person");
+        final Matcher excludeFilter = filter.matcher();
+
+        reader.setExcludeFilter(excludeFilter);
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test the setIncludeAttribute Attributes included must be the only ones
+     * present in the entry. First line dn must be present.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetIncludeAttributeWithMatch() throws Exception {
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setIncludeAttribute(AttributeDescription.valueOf("cn"));
+        reader.setIncludeAttribute(AttributeDescription.valueOf("sn"));
+        reader.setIncludeAttribute(AttributeDescription.valueOf("sn"));
+        final Entry entry = reader.readEntry();
+
+        assertThat(entry).isNotNull();
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isEqualTo(2);
+        assertThat(entry.getAttribute("cn")).isNotNull();
+        assertThat(entry.getAttribute("sn")).isNotNull();
+        assertThat(entry.getAttribute("description")).isNull();
+
+        reader.close();
+    }
+
+    /**
+     * Test the setIncludeAttribute Attributes included must be the only ones
+     * present in the entry. In this case, the attribute "manager" doesn't
+     * exist. Only dn line must be read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetIncludeAttributeWithNoMatch() throws Exception {
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setIncludeAttribute(AttributeDescription.valueOf("manager"));
+        final Entry entry = reader.readEntry();
+
+        assertThat(entry).isNotNull();
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isEqualTo(0);
+        assertThat(entry.getAttribute("description")).isNull();
+
+        reader.close();
+    }
+
+    /**
+     * Test SetIncludeAttribute method of LDIFEntryReader Throws a
+     * NullPointerException if the includeAttribute is null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetIncludeAttributeDoesntAllowNull() throws Exception {
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setIncludeAttribute(null);
+        reader.close();
+    }
+
+    /**
+     * Test SetIncludeBranch method of LDIFEntryReader. "dc=example,dc=org" not
+     * existing in the standard ldif entry. Entry must not be read.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NoSuchElementException.class)
+    public void testSetIncludeBranchWithNoMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setIncludeBranch(DN.valueOf("dc=example,dc=org"));
+
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test SetIncludeBranch method of LDIFEntryReader. "dc=example,dc=com" is
+     * the branch of the standard entry. Entry must be fully read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetIncludeBranchWithMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setIncludeBranch(DN.valueOf("dc=example,dc=com"));
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry).isNotNull();
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+    }
+
+    /**
+     * Test SetIncludeBranch method of LDIFEntryReader. Throws a
+     * NullPointerException if the includeBranch is null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetIncludeBranchDoesntAllowNull() throws Exception {
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setIncludeBranch(null);
+    }
+
+    /**
+     * LDIFEntryReader setIncludeFilter with an equality filter on the
+     * objectclass: vip, Entry musn't be read.
+     *
+     * @throws Exception
+     *             NoSuchElementException launched if entry is not read
+     */
+    @Test(expectedExceptions = NoSuchElementException.class)
+    public void testSetIncludeFilterWithNoMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        final Filter filter = Filter.equality("objectclass", "vip");
+        final Matcher includeFilter = filter.matcher();
+        reader.setIncludeFilter(includeFilter);
+        Entry entry = null;
+        try {
+            entry = reader.readEntry();
+        } finally {
+            reader.close();
+        }
+        assertThat(entry).isNull();
+
+    }
+
+    /**
+     * LDIFEntryReader setIncludeFilter with an equality filter on the
+     * objectclass: person, Entry must be read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetIncludeFilterWithMatch() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        final Filter filter = Filter.equality("objectclass", "person");
+        final Matcher includeFilter = filter.matcher();
+        reader.setIncludeFilter(includeFilter);
+        Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttribute("cn")).isNotNull();
+        assertThat(entry.getAttribute("sn")).isNotNull();
+
+    }
+
+    /**
+     * LDIFEntryReader setIncludeFilter doesn't allow null.
+     *
+     * @throws Exception
+     *             NullPointerException expected
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetIncludeFilterDoesntAllowNull() throws Exception {
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setIncludeFilter(null);
+    }
+
+    /**
+     * Tests reading a malformed record invokes the rejected record listener.
+     *
+     * @throws Exception
+     *             if an unexpected error occurred.
+     */
+    @Test()
+    public void testRejectedLDIFListenerMalformedFirstRecord() throws Exception {
+        RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
+
+        LDIFEntryReader reader =
+                new LDIFEntryReader("dn: baddn", "changetype: add", "objectClass: top",
+                        "objectClass: domainComponent", "dc: example");
+
+        reader.setRejectedLDIFListener(listener);
+
+        assertThat(reader.hasNext()).isFalse();
+
+        verify(listener).handleMalformedRecord(
+                eq(1L),
+                eq(Arrays.asList("dn: baddn", "changetype: add", "objectClass: top",
+                        "objectClass: domainComponent", "dc: example")),
+                any(LocalizableMessage.class));
+        reader.close();
+    }
+
+    /**
+     * Tests reading a malformed LDIF invokes the rejected LDIF listener.
+     *
+     * @throws Exception
+     *             if an unexpected error occurred.
+     */
+    @Test()
+    public void testRejectedLDIFListenerMalformedSecondRecord() throws Exception {
+        RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
+
+        // @formatter:off
+        LDIFEntryReader reader = new LDIFEntryReader(
+                "dn: dc=example,dc=com",
+                "changetype: add",
+                "objectClass: top",
+                "objectClass: domainComponent",
+                "dc: example",
+                "",
+                "dn: baddn",
+                "changetype: add",
+                "objectClass: top",
+                "objectClass: domainComponent",
+                "dc: example"
+        );
+        // @formatter:on
+
+        reader.setRejectedLDIFListener(listener);
+
+        reader.readEntry(); // Skip good record.
+        assertThat(reader.hasNext()).isFalse();
+
+        verify(listener).handleMalformedRecord(
+                eq(7L),
+                eq(Arrays.asList("dn: baddn", "changetype: add", "objectClass: top",
+                        "objectClass: domainComponent", "dc: example")),
+                any(LocalizableMessage.class));
+        reader.close();
+    }
+
+    /**
+     * Tests reading a LDIF which does not conform to the schema invokes the
+     * rejected LDIF listener.
+     *
+     * @throws Exception
+     *             if an unexpected error occurred.
+     */
+    @Test()
+    public void testRejectedRecordListenerRejectsBadSchemaRecord() throws Exception {
+        RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
+
+        // @formatter:off
+        LDIFEntryReader reader = new LDIFEntryReader(
+            "dn: dc=example,dc=com",
+            "changetype: add",
+            "objectClass: top",
+            "objectClass: domainComponent",
+            "dc: example",
+            "xxx: unknown attribute");
+        reader.setRejectedLDIFListener(listener)
+             .setSchemaValidationPolicy(
+                 SchemaValidationPolicy.ignoreAll()
+                     .checkAttributesAndObjectClasses(Policy.REJECT));
+        // @formatter:on
+
+        assertThat(reader.hasNext()).isFalse();
+
+        verify(listener).handleSchemaValidationFailure(
+                eq(1L),
+                eq(Arrays.asList("dn: dc=example,dc=com", "changetype: add", "objectClass: top",
+                        "objectClass: domainComponent", "dc: example", "xxx: unknown attribute")),
+                anyListOf(LocalizableMessage.class));
+        reader.close();
+    }
+
+    /**
+     * Tests reading a LDIF which does not conform to the schema invokes the
+     * rejected LDIF listener.
+     *
+     * @throws Exception
+     *             if an unexpected error occurred.
+     */
+    @Test()
+    public void testRejectedLDIFListenerWarnsBadSchemaRecord() throws Exception {
+
+        RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
+
+        LDIFEntryReader reader =
+                new LDIFEntryReader("dn: dc=example,dc=com", "changetype: add", "objectClass: top",
+                        "objectClass: domainComponent", "dc: example", "xxx: unknown attribute");
+        reader.setRejectedLDIFListener(listener).setSchemaValidationPolicy(
+                SchemaValidationPolicy.ignoreAll().checkAttributesAndObjectClasses(Policy.WARN));
+
+        assertThat(reader.hasNext()).isTrue();
+
+        Entry entry = reader.readEntry();
+
+        assertThat(entry.getName().toString()).isEqualTo("dc=example,dc=com");
+        assertThat(entry.containsAttribute("objectClass", "top", "domainComponent")).isTrue();
+        assertThat(entry.containsAttribute("dc", "example")).isTrue();
+        assertThat(entry.getAttributeCount()).isEqualTo(2);
+
+        verify(listener).handleSchemaValidationWarning(
+                eq(1L),
+                eq(Arrays.asList("dn: dc=example,dc=com", "changetype: add", "objectClass: top",
+                        "objectClass: domainComponent", "dc: example", "xxx: unknown attribute")),
+                anyListOf(LocalizableMessage.class));
+        reader.close();
+    }
+
+    /**
+     * LDIFEntryReader setRejectedLDIFListener skips the record.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testRejectedLDIFListenerSkipsRecord() throws Exception {
+
+        RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
+
+        LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setRejectedLDIFListener(listener).setExcludeBranch(DN.valueOf("dc=com"));
+
+        assertThat(reader.hasNext()).isFalse();
+
+        verify(listener).handleSkippedRecord(eq(1L), eq(Arrays.asList(getStandardEntry())),
+                any(LocalizableMessage.class));
+        reader.close();
+    }
+
+    /**
+     * LDIFEntryReader setIncludeFilter allows null.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetRejectedLDIFListenerDoesAllowNull() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setRejectedLDIFListener(null);
+        Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+
+    }
+
+    /**
+     * LDIFEntryReader setSchemaValidationPolicy. Validate the entry depending
+     * of the selected policy. Entry is here NOT allowed because it contains a
+     * uid attribute which is not allowed by the SchemaValidationPolicy.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testSetSchemaValidationPolicyDefaultRejectsEntry() throws Exception {
+        // @formatter:off
+        String[] strEntry = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com", "objectClass: person",
+            "objectClass: top", "cn: Aaccf Amar", "sn: Amar", "uid: user.0"
+        };
+        // @formatter:on
+        final LDIFEntryReader reader = new LDIFEntryReader(strEntry);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFEntryReader setSchemaValidationPolicy. Validate the entry depending
+     * of the selected policy. Entry is here allowed because it fills the case
+     * of the validation.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetSchemaValidationPolicyDefaultAllowsEntry() throws Exception {
+
+        // @formatter:off
+        String[] strEntry = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com", "objectClass: person",
+            "objectClass: top", "cn: Aaccf Amar", "sn: Amar"
+        };
+        // @formatter:on
+
+        final LDIFEntryReader reader = new LDIFEntryReader(strEntry);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+        final Entry entry = reader.readEntry();
+        reader.close();
+
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isEqualTo(3);
+    }
+
+    /**
+     * LDIFEntryReader SetValidationPolicy doesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetSchemaValidationPolicyDoesntAllowNull() throws Exception {
+
+        // @formatter:off
+        String[] strEntry = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com", "objectClass: person",
+            "objectClass: top", "cn: Aaccf Amar", "sn: Amar", "uid: user.0"
+        };
+        // @formatter:on
+
+        final LDIFEntryReader reader = new LDIFEntryReader(strEntry);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(null);
+
+    }
+
+    /**
+     * Test the setSchemaSetValidationPolicy. Adding a new schema and insuring
+     * the validationPolicy allows the new attribute/class Adding a new schema
+     * explained in the admin-guide (chapter 15. Managing Schema). The new
+     * attribute is accepted by the policy schema. Entry must be read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testSetSchemaSetSchemaValidationPolicyDefaultAllowsEntryWithNewAttribute()
+            throws Exception {
+
+        // @formatter:off
+        final String[] strEntry = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com",
+            "objectClass: person",
+            "objectClass: organizationalperson",
+            "objectClass: top", "cn: Aaccf Amar",
+            "sn: Amar",
+            "objectClass: myCustomObjClass",
+            "myCustomAttribute: Testing..."
+        };
+        // @formatter:on
+
+        final SchemaBuilder scBuild = new SchemaBuilder();
+        // Adding the new schema containing the customclass
+        scBuild.addObjectClass("( temporary-fake-oc-id NAME 'myCustomObjClass"
+                + "' SUP top AUXILIARY MAY myCustomAttribute )", false);
+        scBuild.addAttributeType("( temporary-fake-attr-id NAME 'myCustomAttribute' EQUALITY case"
+                + "IgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstrings"
+                + "Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications )", false);
+        // Adding default core schema
+        scBuild.addSchema(Schema.getCoreSchema(), false);
+        Schema schema = scBuild.toSchema();
+        final LDIFEntryReader reader = new LDIFEntryReader(strEntry);
+        reader.setSchema(schema);
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        Entry entry = null;
+        try {
+            entry = reader.readEntry();
+            // cn + sn + myCustomAttribute + objectClass
+            assertThat(entry.getAttributeCount()).isEqualTo(4);
+            assertThat(entry.getName().toString()).isEqualTo(
+                    "uid=user.0,ou=People,dc=example,dc=com");
+            assertThat(entry.getAttribute("sn").firstValue().toString()).isEqualTo("Amar");
+            assertThat(entry.getAttribute("cn").firstValueAsString()).isEqualTo("Aaccf Amar");
+            // entry.getAttribute("new attribute") : access by that way doesn't work...
+            // TODO BUG jira/browse/OPENDJ-157
+            assertThat(
+                    entry.getAttribute(AttributeDescription.valueOf("myCustomAttribute", schema))
+                            .firstValueAsString()).isEqualTo("Testing...");
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test the setSchemaSetValidationPolicy : throw an exception if
+     * unrecognized attributes are found. ex. Entry
+     * "uid=user.0,ou=People,dc=example,dc=com" doesn't respect the schema
+     * because it contains an unrecognized object class "myCustomObjClass".
+     * Entry musn't be read.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testSetSchemaSetSchemaValidationPolicyDefaultDoesntAllowEntryWithNewAttribute()
+            throws Exception {
+
+        // @formatter:off
+        String[] strEntry = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com", "objectClass: person",
+            "objectClass: organizationalperson", "objectClass: top", "cn: Aaccf Amar",
+            "sn: Amar", "objectClass: myCustomObjClass", "myCustomAttribute: Testing..."
+        };
+        // @formatter:on
+
+        final LDIFEntryReader reader = new LDIFEntryReader(strEntry);
+
+        final SchemaBuilder scBuild = new SchemaBuilder();
+        scBuild.addSchema(Schema.getCoreSchema(), false);
+
+        reader.setSchema(scBuild.toSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFEntryReader setSchema doesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testSetSchemaDoesntAllowNull() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(getStandardEntry());
+        reader.setSchema(null); // must throw a NullPointerException
+    }
+
+    /**
      * Tests readEntry method of LDIFEntryReader class.See
      * https://opends.dev.java.net/issues/show_bug.cgi?id=4545 for more details.
      *
      * @throws Exception
      *             If the test failed unexpectedly.
      */
-    @Test()
+    @Test(expectedExceptions = NoSuchElementException.class)
     public void testEmpty() throws Exception {
         final String path = TestCaseUtils.createTempFile("");
         final FileInputStream in = new FileInputStream(path);
@@ -56,13 +904,124 @@
         try {
             Assert.assertFalse(reader.hasNext());
             Assert.assertFalse(reader.hasNext());
-            try {
-                reader.readEntry();
-                Assert.fail("reader.readEntry() should have thrown NoSuchElementException");
-            } catch (NoSuchElementException e) {
-                // This is expected.
-            }
+            reader.readEntry();
+        } finally {
             Assert.assertFalse(reader.hasNext());
+            reader.close();
+        }
+    }
+
+    /**
+     * Test to read an entry with no empty spaces.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testReadEntryWithNoSpaces() throws Exception {
+        // @formatter:off
+        final String[] strEntry = {
+            "# Entry of SCarter",
+            "dn:uid=scarter,ou=People,dc=example,dc=com",
+            "objectClass:person",
+            "objectClass:inetorgperson",
+            "objectClass:organizationalperson",
+            "objectClass:top",
+            "postalAddress:Aaccf Amar$01251 Chestnut Street$Panama City, DE  50369",
+            "postalCode:50369",
+            "uid:scarter",
+            "description::U2hvcnQgZGVzY3JpcHRpb24gb2YgU2NhcnRlcg=="
+        };
+        // @formatter:on
+        final String path = TestCaseUtils.createTempFile(strEntry);
+        final FileInputStream in = new FileInputStream(path);
+        final LDIFEntryReader reader = new LDIFEntryReader(in);
+        Entry entry = null;
+        try {
+            assertThat(reader.hasNext());
+            entry = reader.readEntry();
+            assertThat(entry.getName().toString()).isEqualTo(
+                    "uid=scarter,ou=People,dc=example,dc=com");
+            assertThat(entry.getAttribute("uid").firstValueAsString()).isEqualTo("scarter");
+            assertThat(entry.getAttribute("description").firstValueAsString()).isEqualTo(
+                    "Short description of Scarter");
+        } finally {
+            reader.close();
+        }
+
+    }
+
+    /**
+     * Test to read an entry containing spaces before the attribute
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testReadEntryWithAttributesSpacesAtStart() throws Exception {
+        // @formatter:off
+        final String[] strEntry = {
+            "#   Entry of SCarter",
+            "dn:   uid=scarter,ou=People,dc=example,dc=com",
+            "objectClass:   person",
+            "objectClass:   inetorgperson",
+            "objectClass:   organizationalperson",
+            "objectClass:   top",
+            "postalAddress:   Aaccf Amar$01251 Chestnut Street$Panama City, DE  50369",
+            "postalCode:   50369",
+            "uid:    scarter",
+            "description::    U2hvcnQgZGVzY3JpcHRpb24gb2YgU2NhcnRlcg==",
+        };
+        // @formatter:on
+        final String path = TestCaseUtils.createTempFile(strEntry);
+        final FileInputStream in = new FileInputStream(path);
+        final LDIFEntryReader reader = new LDIFEntryReader(in);
+        Entry entry = null;
+        try {
+            assertThat(reader.hasNext());
+            entry = reader.readEntry();
+            assertThat(entry.getName().toString()).isEqualTo(
+                    "uid=scarter,ou=People,dc=example,dc=com");
+            assertThat(entry.getAttribute("uid").firstValueAsString()).isEqualTo("scarter");
+            assertThat(entry.getAttribute("description").firstValueAsString()).isEqualTo(
+                    "Short description of Scarter");
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test to read an entry containing spaces at the end of the attribute. ldif
+     * do not admit spaces at end ;)
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testReadEntryWithAttributesSpacesAtEnd() throws Exception {
+        // @formatter:off
+        final String[] strEntry = {
+            "#   Entry of SCarter   ",
+            "dn:   uid=scarter,ou=People,dc=example,dc=com    ",
+            "objectClass:   person    ",
+            "objectClass:   inetorgperson    ",
+            "objectClass:   organizationalperson    ",
+            "objectClass:   top  ",
+            "postalAddress:   Aaccf Amar$01251 Chestnut Street$Panama City, DE  50369  ",
+            "postalCode:   50369 ",
+            "uid:    scarter  ",
+            "description::    U2hvcnQgZGVzY3JpcHRpb24gb2YgU2NhcnRlcg==   ",
+        };
+        // @formatter:on
+        final String path = TestCaseUtils.createTempFile(strEntry);
+        final FileInputStream in = new FileInputStream(path);
+        final LDIFEntryReader reader = new LDIFEntryReader(in);
+        Entry entry = null;
+        try {
+            assertThat(reader.hasNext());
+            entry = reader.readEntry();
+            assertThat(entry.getName().toString()).isEqualTo(
+                    "uid=scarter,ou=People,dc=example,dc=com");
+            assertThat(entry.getAttribute("uid").firstValueAsString()).isEqualTo("scarter");
+            assertThat(entry.getAttribute("description").firstValueAsString()).isEqualTo(
+                    "Short description of Scarter");
         } finally {
             reader.close();
         }
@@ -75,34 +1034,614 @@
      * @throws Exception
      *             If the test failed unexpectedly.
      */
-    @Test()
+    @Test(expectedExceptions = NoSuchElementException.class)
     public void testReadEntry() throws Exception {
-        final String path =
-                TestCaseUtils.createTempFile("dn: uid=1,ou=people,dc=ucsf,dc=edu",
-                        "objectClass: top", "objectClass: person",
-                        "objectClass: organizationalperson", "objectClass: inetorgperson",
-                        "givenName: Aaccf", "sn: Amar", "cn: Aaccf Amar", "initials: ASA",
-                        "employeeNumber: 020000001", "uid: 1", "mail: Aaccf.Amar@ucsf.edu",
-                        "userPassword: password", "telephoneNumber: +1 685 622 6202",
-                        "homePhone: +1 225 216 5900", "pager: +1 779 041 6341",
-                        "mobile: +1 010 154 3228", "street: 01251 Chestnut Street",
-                        "l: Panama City", "st: DE", "postalCode: 50369",
-                        "postalAddress: Aaccf Amar$01251 Chestnut Street$Panama City, DE  50369",
-                        "description: This is the description for Aaccf Amar.");
+        final String path = TestCaseUtils.createTempFile(getStandardEntry());
         final FileInputStream in = new FileInputStream(path);
         final LDIFEntryReader reader = new LDIFEntryReader(in);
         try {
             Assert.assertTrue(reader.hasNext());
             final Entry entry = reader.readEntry();
             assertNotNull(entry);
-            Assert.assertEquals(entry.getName(), DN.valueOf("uid=1,ou=people,dc=ucsf,dc=edu"));
+            Assert.assertEquals(entry.getName(), DN
+                    .valueOf("uid=user.0,ou=People,dc=example,dc=com"));
             Assert.assertFalse(reader.hasNext());
-            try {
-                reader.readEntry();
-                Assert.fail("reader.readEntry() should have thrown NoSuchElementException");
-            } catch (NoSuchElementException e) {
-                // This is expected.
-            }
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test to read an entry containing duplicates values
+     * ERR_LDIF_MULTI_VALUED_SINGLE_VALUED_ATTRIBUTE &&
+     * WARN_LDIF_DUPLICATE_ATTRIBUTE_VALUE
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testLDIFEntryReaderEntryWithDuplicateAttributes() throws Exception {
+        // @formatter:off
+        final String[] strEntry = {
+            "dn: cn=user.0,ou=People,dc=example,dc=com",
+            "objectClass: organizationalperson",
+            "objectClass: top",
+            "postalAddress: Aaccf Amar$01251 Chestnut Street$Panama City, DE  50369",
+            "postalCode: 50369",
+            "description: This is the description for Aaccf Amar.",
+            "userPassword: ;", // empty value allowed
+            "telephoneNumber: +1 685 622 6202", "sn: Amar",
+            // entryUUID : ERR_LDIF_MULTI_VALUED_SINGLE_VALUED_ATTRIBUTE
+            "entryUUID: ad55a34a-763f-358f-93f9-da86f9ecd9e4",
+            "entryUUID: ad55a34a-763f-358f-93f9-da45f9ecd9e4",
+            // WARN_LDIF_DUPLICATE_ATTRIBUTE_VALUE :
+            "objectClass: person",
+            "objectClass: person"
+        };
+        // @formatter:on
+        final String path = TestCaseUtils.createTempFile(strEntry);
+        final FileInputStream in = new FileInputStream(path);
+        final LDIFEntryReader reader = new LDIFEntryReader(in);
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFEntryReader - Try to read a full example of entry.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFEntryReaderFullEntry() throws Exception {
+
+        // @formatter:off
+        final String[] strEntry = {
+            "version: 1",
+            "dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Barbara Jensen",
+            "cn: Barbara J Jensen",
+            "cn: Babs Jensen",
+            "sn: Jensen",
+            "uid: bjensen",
+            "telephonenumber: +1 408 555 1212",
+            "description: A big sailing fan.",
+            "", // if a space here, second entry is not read
+            "dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Bjorn Jensen",
+            "sn: Jensen",
+            "telephonenumber: +1 408 555 1212"
+        };
+        // @formatter:on
+        final String path = TestCaseUtils.createTempFile(strEntry);
+        final FileInputStream in = new FileInputStream(path);
+        final LDIFEntryReader reader = new LDIFEntryReader(in);
+
+        Entry entry = null;
+        try {
+            assertThat(reader.hasNext());
+            // 1st entry
+            entry = reader.readEntry();
+            assertThat(entry.getName().toString()).isEqualTo(
+                    "cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com");
+            assertThat(entry.getAttributeCount()).isEqualTo(6);
+            // 2nd
+            entry = reader.readEntry();
+            assertThat(entry.getName().toString()).isEqualTo(
+                    "cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com");
+            assertThat(entry.getAttributeCount()).isEqualTo(4);
+
+            assertThat(reader.hasNext()).isFalse();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Testing to read an entry which containing empty required attributes.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testValueOfLDIFEntryReadStandardEntryMissingValues() throws Exception {
+
+        // @formatter:off
+        final String[] strEntry = {
+            "dn: uid=user.0,ou=People,dc=example,dc=com",
+            "objectClass: person",
+            "objectClass: organizationalperson",
+            "objectClass: top",
+            "cn: Aaccf Amar",
+            "sn:"
+        };
+        // @formatter:on
+        final LDIFEntryReader reader = new LDIFEntryReader(strEntry);
+        reader.setSchema(Schema.getDefaultSchema());
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+
+    }
+
+    /**
+     * Testing to read an entry containing BER value
+     * schemaValidationPolicy.checkAttributeValues().needsChecking() &&
+     * attributeDescription.containsOption("binary") reply 'because it has an
+     * unexpected binary option for attribute sn : 'sn;binary'.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testValueOfLDIFEntryBERUnexpectedBinaryOption() throws Exception {
+
+        // @formatter:off
+        final String[] strEntry = {
+            "version: 1",
+            "dn:: b3U95Za25qWt6YOoLG89QWlyaXVz",
+            "# dn:: ou=<JapaneseOU>,o=Airius",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Horatio Jensen",
+            "cn: Horatio N Jensen",
+            "sn: Jensen",
+            "uid: hjensen",
+            "sn;binary:: 5bCP56yg5Y6f"
+        };
+        // @formatter:on
+
+        final LDIFEntryReader reader = new LDIFEntryReader(strEntry);
+        Schema schema = Schema.getCoreSchema();
+        reader.setSchema(schema);
+        reader.setSchemaValidationPolicy(SchemaValidationPolicy.defaultPolicy());
+
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+
+    }
+
+    /**
+     * Testing to read an entry containing a fatal continuation line at start.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testValueOfLDIFEntryFatalContinuationLineAtStart() throws Exception {
+
+        // @formatter:off
+        final String[] strEntry = {
+            " This is a fatal continuation line at start",
+            "dn:: b3U95Za25qWt6YOoLG89QWlyaXVz",
+            "# dn:: ou=<JapaneseOU>,o=Airius",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Horatio Jensen",
+            "cn: Horatio N Jensen",
+            "sn: Jensen",
+            "uid: hjensen"
+        };
+        // @formatter:on
+
+        final LDIFEntryReader reader = new LDIFEntryReader(strEntry);
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+
+    }
+
+    /**
+     * LDIFEntryReader entry containing a reference to an external file.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testValueOfLDIFEntryReadEntryContainingURL() throws Exception {
+        // @formatter:off
+        final LDIFEntryReader reader = new LDIFEntryReader(
+                "#A single comment",
+                " continued in the second line",
+                "version: 1",
+                "dn:: b3U95Za25qWt6YOoLG89QWlyaXVz",
+                "# dn:: ou=<JapaneseOU>,o=Airius",
+                "objectclass: top",
+                "objectclass: person",
+                "objectclass: organizationalPerson",
+                "cn: Horatio Jensen",
+                "cn: Horatio N Jensen",
+                "sn: Jensen",
+                "uid: hjensen",
+                "telephonenumber: +1 408 555 1212",
+                "jpegphoto:< http://www.forgerock.com/sites/default/files/forgerock_logo.png",
+                "#This is a end line comment", "# Followed by another"
+        );
+        // @formatter:on
+
+        try {
+            Entry entry = reader.readEntry();
+            assertThat(entry.getName().toString()).isNotEqualTo("b3U95Za25qWt6YOoLG89QWlyaXVz");
+            assertThat(entry.getAttributeCount()).isEqualTo(6);
+            assertThat(entry.getAttribute("jpegphoto")).isNotEmpty();
+            assertThat(entry.getAttribute("cn").firstValueAsString()).isEqualTo("Horatio Jensen");
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFEntryReader entry containing a reference to an external file & an
+     * invalid protocol.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testValueOfLDIFEntryReadEntryContainingURLInvalidProtocol() throws Exception {
+
+        // @formatter:off
+        final LDIFEntryReader reader = new LDIFEntryReader(
+                "version: 1",
+                "dn:: b3U95Za25qWt6YOoLG89QWlyaXVz",
+                "# dn:: ou=<JapaneseOU>,o=Airius",
+                "objectclass: top",
+                "objectclass: person",
+                "objectclass: organizationalPerson",
+                "cn: Horatio Jensen",
+                "cn: Horatio N Jensen",
+                "sn: Jensen",
+                "uid: hjensen",
+                "telephonenumber: +1 408 555 1212",
+                "jpegphoto:< invalidProtocol",
+                " ",
+                " ");
+        // @formatter:on
+
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test to read an entry missing key value.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testReadEntryParseColonPositionThrowException() throws Exception {
+
+        // @formatter:off
+        final String path = TestCaseUtils.createTempFile(
+                "#Entry made for testing",
+                ": cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com",
+                "objectclass: top",
+                "objectclass: person",
+                "objectclass: organizationalPerson"
+        );
+        // @formatter:on
+
+        final FileInputStream in = new FileInputStream(path);
+        final LDIFEntryReader reader = new LDIFEntryReader(in);
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test to read an entry containing base64 encoded attribute.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testReadEntryBase64EncodedMalformedBase64Attribute() throws Exception {
+
+        // @formatter:off
+        final LDIFEntryReader reader = new LDIFEntryReader(Arrays.asList(
+            "version: 1",
+            "dn: cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Gern Jensen",
+            "cn: Gern O Jensen",
+            "sn: Jensen",
+            "uid: gernj",
+            "telephonenumber: +1 408 555 1212",
+            "description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl"
+            + "IGlzIGJhc2UtNjQtZW5aaaaaaaaaaajb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG"
+            + "VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg"
+            + "b3V0IG1vcmUu"
+        ));
+        // @formatter:on
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test to read an entry containing base64 encoded attribute.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testReadEntryBase64Encoded() throws Exception {
+
+        // @formatter:off
+        final LDIFEntryReader reader = new LDIFEntryReader(Arrays.asList(
+            "version: 1",
+            "dn: cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Gern Jensen",
+            "cn: Gern O Jensen",
+            "sn: Jensen",
+            "uid: gernj",
+            "telephonenumber: +1 408 555 1212",
+            "description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl"
+            + "IGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG"
+            + "VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg"
+            + "b3V0IG1vcmUu")
+        );
+        // @formatter:on
+
+        try {
+            assertThat(reader.hasNext());
+            final Entry entry = reader.readEntry();
+            assertThat(entry).isNotNull();
+            assertThat(entry.getAttributeCount()).isEqualTo(6);
+            // Verifying second occurrence of is not taken into account
+            assertThat(entry.getAttribute("cn").firstValueAsString()).isEqualTo("Gern Jensen");
+            // Verifying decoding is enabled on description attribute
+            assertThat(entry.getAttribute("description").firstValueAsString())
+                    .isNotSameAs(
+                            "V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl"
+                                    + "IGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG"
+                                    + "VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg"
+                                    + "b3V0IG1vcmUu");
+            assertThat(entry.getAttribute("description").firstValueAsString()).contains(
+                    "What a careful reader you are!");
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test to read an entry containing base64 encoded attribute.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testReadEntryBase64EncodedDN() throws Exception {
+
+        // @formatter:off
+        final LDIFEntryReader reader = new LDIFEntryReader(Arrays.asList(
+            "dn::  dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz", // adding space before ok, after : ko
+            "# dn:: uid=<uid>,ou=<JapaneseOU>,o=Airius",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Gern Jensen",
+            "cn: Gern O Jensen",
+            "sn: Jensen",
+            "uid: gernj"
+        ));
+        // @formatter:on
+        try {
+            assertThat(reader.hasNext());
+            final Entry entry = reader.readEntry();
+            assertThat(reader.hasNext()).isFalse();
+            assertThat(entry.getName().toString()).isEqualTo("uid=rogasawara,ou=営業部,o=Airius");
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test to read an entry containing base64 encoded DN. DN base64 encoded is
+     * malformed. Must throw an error.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = DecodeException.class)
+    public void testReadEntryBase64EncodedDNMalformedThrowsError() throws Exception {
+
+        // @formatter:off
+        final LDIFEntryReader reader = new LDIFEntryReader(Arrays.asList(
+            "dn:: dWlkPXJvZ2FzYXdh!!!OOOpppps!!!25qWt6YOoLG89QWlyaXVz",
+            "# dn:: uid=<uid>,ou=<JapaneseOU>,o=Airius",
+            "objectclass: top",
+            "objectclass: person",
+            "objectclass: organizationalPerson",
+            "cn: Gern Jensen",
+            "cn: Gern O Jensen",
+            "sn: Jensen",
+            "uid: gernj"
+        ));
+        // @formatter:on
+
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Test LDIFEntryReader reading a LDIF entry via EntryAsArray.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testLDIFEntryReaderEntryAsArray() throws Exception {
+
+        final LDIFEntryReader reader = new LDIFEntryReader(Arrays.asList(getStandardEntry()));
+
+        try {
+            assertThat(reader.hasNext());
+            assertThat(reader.readEntry().getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * LDIFEntryReader cause NullPointerException when InputStream is null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testLDIFEntryReaderInpuStreamDoesntAllowNull() throws Exception {
+        final InputStream is = null;
+        LDIFEntryReader reader = new LDIFEntryReader(is);
+    }
+
+    /**
+     * LDIFEntryReader read cause IOException.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = IOException.class)
+    public void testReadEntryThrowsIOException() throws Exception {
+
+        final FileInputStream mockIn = mock(FileInputStream.class);
+        final LDIFEntryReader reader = new LDIFEntryReader(mockIn);
+
+        doThrow(new IOException()).when(mockIn).read();
+        try {
+            reader.readEntry();
+        } finally {
+            reader.close();
+            verify(mockIn, times(1)).close();
+        }
+    }
+
+    /**
+     * LDIFEntryReader ValueOfLDIFEntry - Multiple change records found.
+     * Exception LocalizedIllegalArgumentException expected.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testValueOfLDIFEntryMultipleChangeRecordFound() throws Exception {
+
+        // @formatter:off
+        final Entry entry = LDIFEntryReader.valueOfLDIFEntry(
+            "#This is an example test",
+            "dn: CN=John Smith,OU=Legal,DC=example,DC=com",
+            "changetype: modify",
+            "replace:employeeID",
+            "employeeID: 1234",
+            "",
+            "dn: CN=Jane Smith,OU=Accounting,DC=example,DC=com",
+            "changetype: modify",
+            "replace:employeeID",
+            "employeeID: 5678"
+        );
+        // @formatter:on
+    }
+
+    /**
+     * LDIFEntryReader ValueOfLDIFEntry throws exception when a single comment
+     * inserted.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testValueOfLDIFEntryThrowsExceptionIfOnlyAComment() throws Exception {
+        LDIFEntryReader.valueOfLDIFEntry("#This is an example test");
+    }
+
+    /**
+     * Test of valueOfLDIFEntry using malformed LDIF. Must return an
+     * LocalizedIllegalArgumentException In this case, dn is missing.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testValueOfLDIFEntryMalformedEntry() throws Exception {
+
+        // @formatter:off
+        LDIFEntryReader.valueOfLDIFEntry(
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalperson",
+                "objectClass: inetorgperson"
+        );
+        // @formatter:on
+    }
+
+    /**
+     * Test of valueOfLDIFEntry using well formed entry.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testValueOfLDIFEntryWellFormedEntry() throws Exception {
+        // @formatter:off
+        final Entry entry = LDIFEntryReader.valueOfLDIFEntry(
+                "dn: uid=user.0,ou=People,dc=example,dc=com",
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalperson",
+                "objectClass: inetorgperson"
+        );
+        // @formatter:on
+
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttributeCount()).isEqualTo(1);
+    }
+
+    /**
+     * Test LDIFEntryReader valueOfLDIFEntry on the standard entry and verify if
+     * all the attributes are well read.
+     *
+     * @throws Exception
+     */
+    @Test()
+    public void testValueOfLDIFEntryReadStandardEntry() throws Exception {
+
+        final Entry entry = LDIFEntryReader.valueOfLDIFEntry(getStandardEntry());
+
+        assertThat(entry).isNotNull();
+        assertThat(entry.getName()).isNotNull();
+        assertThat(entry.getName().toString()).isEqualTo("uid=user.0,ou=People,dc=example,dc=com");
+        assertThat(entry.getAttribute("sn").firstValue().toString()).isEqualTo("Amar");
+        assertThat(entry.getAttributeCount()).isEqualTo(nbStandardEntryAttributes);
+    }
+
+    /**
+     * LDIFReader valueOfLDIFEntry doesn't allow null.
+     *
+     * @throws Exception
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testValueOfLDIFEntryDoesntAllowNull() throws Exception {
+        LDIFEntryReader reader = new LDIFEntryReader();
+        try {
+            reader.valueOfLDIFEntry(null);
         } finally {
             reader.close();
         }

--
Gitblit v1.10.0