/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Portions Copyright 2012 ForgeRock AS */ package org.opends.server.util; import static org.opends.server.util.StaticUtils.*; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.util.LinkedList; import java.util.List; import org.opends.messages.Message; import org.opends.server.TestCaseUtils; import org.opends.server.core.DirectoryServer; import org.opends.server.types.Attribute; import org.opends.server.types.Attributes; import org.opends.server.types.DN; import org.opends.server.types.Entry; import org.opends.server.types.LDIFExportConfig; import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.Modification; import org.opends.server.types.RawModification; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** * This class defines a set of tests for the * {@link org.opends.server.util.LDIFWriter} class. */ public final class TestLDIFWriter extends UtilTestCase { // Data used in writeModifyEntry tests. private Object[][] MODIFY_ENTRY_DATA_LDIF; // Data used in writeModifyDNEntry tests. private Object[][] MODIFY_DN_ENTRY_DATA_LDIF; /** * Tests will be performed against a byte array output stream. */ private static final class Writer { // The underlying output stream. private final ByteArrayOutputStream stream; // The underlying LDIF config. private final LDIFExportConfig config; // The LDIF writer. private final LDIFWriter writer; /** * Create a new string writer. */ public Writer() { this.stream = new ByteArrayOutputStream(); this.config = new LDIFExportConfig(stream); try { this.writer = new LDIFWriter(config); } catch (IOException e) { // Should not happen. throw new RuntimeException(e); } } /** * Get the LDIF writer. * * @return Returns the LDIF writer. */ public LDIFWriter getLDIFWriter() { return writer; } /** * Close the writer and get a string reader for the LDIF content. * * @return Returns the string contents of the writer. * @throws Exception * If an error occurred closing the writer. */ public BufferedReader getLDIFBufferedReader() throws Exception { writer.close(); String ldif = stream.toString("UTF-8"); StringReader reader = new StringReader(ldif); return new BufferedReader(reader); } /** * Close the writer and get an LDIF reader for the LDIF content. * * @return Returns an LDIF Reader. * @throws Exception * If an error occurred closing the writer. */ public LDIFReader getLDIFReader() throws Exception { writer.close(); ByteArrayInputStream istream = new ByteArrayInputStream(stream.toByteArray()); LDIFImportConfig config = new LDIFImportConfig(istream); return new LDIFReader(config); } } /** * Once-only initialization. * * @throws Exception * If an unexpected error occurred. */ @BeforeClass public void setUp() throws Exception { // This test suite depends on having the schema available, so we'll // start the server. TestCaseUtils.startServer(); String[] modifyEntryDataLDIF = { "dn: cn=Paula Jensen,ou=Product Development,dc=airius,dc=com\n" + "changetype: modify\n" + "add: postaladdress\n" + "postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086\n" + "-\n" + "delete: description\n" + "-\n" + "replace: telephonenumber\n" + "telephonenumber: +1 408 555 1234\n" + "telephonenumber: +1 408 555 5678\n" + "-\n" + "delete: facsimiletelephonenumber\n" + "facsimiletelephonenumber: +1 408 555 9876\n" + "\n", "dn: cn=Ingrid Jensen,ou=Product Support,dc=airius,dc=com\n" + "changetype: modify\n" + "replace: postaladdress\n" + "-\n" + "delete: description\n" + "\n", "dn: \n" + "changetype: modify\n" + "delete: description\n" + "\n", "dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz\n" + "changetype: modify\n" + "add: description\n" + "description:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz" + "\n" }; List<Object[]> changes = createChangeRecords( ModifyChangeRecordEntry.class, modifyEntryDataLDIF); MODIFY_ENTRY_DATA_LDIF = changes.toArray(new Object[0][]); String[] modifyDNEntryDataLDIF = { "dn: cn=Paula Jensen,ou=Product Development,dc=airius,dc=com\n" + "changetype: modrdn\n" + "newrdn: cn=Paul Jensen\n" + "deleteoldrdn: 1\n", "dn: cn=Ingrid Jensen,ou=Product Support,dc=airius,dc=com\n" + "changetype: moddn\n" + "newrdn: cn=Ingrid Jensen\n" + "deleteoldrdn: 0\n" + "newsuperior: ou=Product Development,dc=airius,dc=com\n" }; changes = createChangeRecords(ModifyDNChangeRecordEntry.class, modifyDNEntryDataLDIF); MODIFY_DN_ENTRY_DATA_LDIF = changes.toArray(new Object[0][]); } /** * Check that creating a writer and closing it immediately does not * write anything. * * @throws Exception * If the test failed unexpectedly. */ @Test public void TestEmptyWriter() throws Exception { Writer writer = new Writer(); Assert.assertNull(writer.getLDIFBufferedReader().readLine()); } /** * LDIF writer - example comment strings. * * @return Returns an array of comment strings and their expected LDIF * form. */ @DataProvider(name = "writeCommentDataProvider") public Object[][] createTestWriteCommentData() { return new Object[][] { { "", 40, new String[] { "# " } }, { "one two three four five six seven " + "eight nine ten eleven twelve thirteen " + "fourteen fifteen sixteen seventeen " + "eighteen nineteen", 40, new String[] { "# one two three four five six seven", "# eight nine ten eleven twelve thirteen", "# fourteen fifteen sixteen seventeen", "# eighteen nineteen" } }, { "one two three four five six seven " + "eight nine ten\neleven twelve thirteen " + "fourteen fifteen\r\nsixteen seventeen " + "eighteen nineteen", 40, new String[] { "# one two three four five six seven", "# eight nine ten", "# eleven twelve thirteen fourteen", "# fifteen", "# sixteen seventeen eighteen nineteen" } }, { "one two three four five six seven " + "eight nine ten eleven twelve thirteen " + "fourteen fifteen sixteen seventeen " + "eighteen nineteen", -1, new String[] { "# one two three four five " + "six seven eight nine ten eleven " + "twelve thirteen fourteen fifteen " + "sixteen seventeen eighteen nineteen" } }, { "onetwothreefourfivesixseven" + "eightnineteneleventwelvethirteen" + "fourteenfifteensixteenseventeen" + "eighteennineteen", 40, new String[] { "# onetwothreefourfivesixseveneightninete", "# neleventwelvethirteenfourteenfifteensi", "# xteenseventeeneighteennineteen" } }, }; } /** * Test the {@link LDIFWriter#writeComment(Message, int)} method. * * @param comment * The input comment string. * @param wrapColumn * The wrap column. * @param expectedLDIF * An array of expected lines. * @throws Exception * If the test failed unexpectedly. */ @Test(dataProvider = "writeCommentDataProvider") public void TestWriteComment(String comment, int wrapColumn, String[] expectedLDIF) throws Exception { Writer writer = new Writer(); LDIFWriter ldifWriter = writer.getLDIFWriter(); ldifWriter.writeComment(Message.raw(comment), wrapColumn); checkLDIFOutput(writer, expectedLDIF); } /** * LDIF writer - sample entry provider. * * @return Returns an array of LDAP entry objects. * @throws Exception If an error occurred whilst constructing the test entries. */ @DataProvider(name = "entryDataProvider") public Object[][] createTestEntryData() throws Exception { String[][] input = { { "cn=john smith, dc=com", "objectclass", "top", "objectclass", "person", "cn", "john smith", "sn", "smith", "description", "description of john" }, { "", "objectclass", "top", "objectClass", "ds-root-dse", }, }; List<Entry[]> entries = new LinkedList<Entry[]>(); for (String[] s : input) { DN dn = DN.decode(s[0]); Entry entry = new Entry(dn, null, null, null); for (int i = 1; i < s.length; i+=2) { String atype = toLowerCase(s[i]); String avalue = toLowerCase(s[i+1]); if (atype.equals("objectclass")) { entry.addObjectClass(DirectoryServer.getObjectClass(avalue)); } else { Attribute attr = Attributes.create(atype, avalue); // Assume that there will be no duplicates. entry.addAttribute(attr, null); } } entries.add(new Entry[]{ entry }); } return entries.toArray(new Object[0][]); } /** * Test the {@link LDIFWriter#writeEntry(Entry)} method. * * @param entry * The entry to ouput. * @throws Exception * If the test failed unexpectedly. */ @Test(dataProvider = "entryDataProvider") public void TestWriteEntry(Entry entry) throws Exception { // FIXME: This test need more work. It should really check that the // LDIF output is correct, rather than re-parsing it, because the // parser could be tolerant to malformed LDIF output. Writer writer = new Writer(); LDIFWriter ldifWriter = writer.getLDIFWriter(); ldifWriter.writeEntry(entry); LDIFReader reader = writer.getLDIFReader(); Entry readEntry = reader.readEntry(); reader.close(); Assert.assertEquals(readEntry.getDN(), entry.getDN()); } /** * Test the {@link LDIFWriter#writeAddChangeRecord(Entry)} method. * * @param entry * The entry to ouput. * @throws Exception * If the test failed unexpectedly. */ @Test(dataProvider = "entryDataProvider") public void TestWriteAddEntry(Entry entry) throws Exception { // FIXME: This test need more work. It should really check that the // LDIF output is correct, rather than re-parsing it, because the // parser could be tolerant to malformed LDIF output. Writer writer = new Writer(); LDIFWriter ldifWriter = writer.getLDIFWriter(); ldifWriter.writeAddChangeRecord(entry); LDIFReader reader = writer.getLDIFReader(); ChangeRecordEntry add = reader.readChangeRecord(false); reader.close(); Assert.assertTrue(add instanceof AddChangeRecordEntry); Assert.assertEquals(add.getDN(), entry.getDN()); } /** * LDIF writer - sample modification provider. * * @return Returns an array of LDAP modification objects. * @throws Exception If an error occurred whilst constructing the test entries. */ @DataProvider(name = "writeModifyDataProvider") public Object[][] createTestWriteModifyData() throws Exception { return MODIFY_ENTRY_DATA_LDIF; } /** * Test the {@link LDIFWriter#writeModifyChangeRecord(DN, List)} * method. * * @param change * The modification change record. * @param expectedLDIF * An array of expected lines. * @throws Exception * If the test failed unexpectedly. */ @Test(dataProvider = "writeModifyDataProvider") public void TestWriteModifyChangeRecord(ModifyChangeRecordEntry change, String[] expectedLDIF) throws Exception { Writer writer = new Writer(); LDIFWriter ldifWriter = writer.getLDIFWriter(); List<Modification> mods = new LinkedList<Modification>(); for (RawModification lmod : change.getModifications()) { mods.add(lmod.toModification()); } ldifWriter.writeModifyChangeRecord(change.getDN(), mods); checkLDIFOutput(writer, expectedLDIF); } /** * Test the {@link LDIFWriter#writeDeleteChangeRecord(Entry, boolean)} method. * * @param entry * The entry to ouput. * @throws Exception * If the test failed unexpectedly. */ @Test(dataProvider = "entryDataProvider") public void TestWriteDeleteEntry(Entry entry) throws Exception { Writer writer = new Writer(); LDIFWriter ldifWriter = writer.getLDIFWriter(); ldifWriter.writeDeleteChangeRecord(entry, false); String[] expectedLDIF = new String[] { "dn: " + entry.getDN(), "changetype: delete" }; checkLDIFOutput(writer, expectedLDIF); } /** * LDIF writer - sample modification DN provider. * * @return Returns an array of LDAP modification DN objects. * @throws Exception If an error occurred whilst constructing the test entries. */ @DataProvider(name = "writeModifyDNDataProvider") public Object[][] createTestWriteModifyDNData() throws Exception { return MODIFY_DN_ENTRY_DATA_LDIF; } /** * Test the {@link LDIFWriter#writeModifyChangeRecord(DN, List)} * method. * * @param change * The modification change record. * @param expectedLDIF * An array of expected lines. * @throws Exception * If the test failed unexpectedly. */ @Test(dataProvider = "writeModifyDNDataProvider") public void TestWriteModifyDNChangeRecord( ModifyDNChangeRecordEntry change, String[] expectedLDIF) throws Exception { Writer writer = new Writer(); LDIFWriter ldifWriter = writer.getLDIFWriter(); ldifWriter.writeModifyDNChangeRecord(change.getDN(), change.getNewRDN(), change.deleteOldRDN(), change .getNewSuperiorDN()); checkLDIFOutput(writer, expectedLDIF); } /** * Close the LDIF writer and read its content and check it against the * expected output. * * @param writer * The LDIF writer. * @param expectedLDIF * The expected LDIF output. * @throws Exception * If an unexpected exception occurred. */ private void checkLDIFOutput(Writer writer, String[] expectedLDIF) throws Exception { BufferedReader reader = writer.getLDIFBufferedReader(); StringBuilder expected = new StringBuilder(); StringBuilder actual = new StringBuilder(); boolean failed = false; for (String expectedLine : expectedLDIF) { String actualLine = reader.readLine(); if (!failed && !actualLine.equals(expectedLine)) { failed = true; } expected.append(" "); expected.append(expectedLine); expected.append("\n"); actual.append(" "); actual.append(actualLine); actual.append("\n"); } String actualLine = reader.readLine(); while (actualLine != null) { if (actualLine.trim().length() != 0) { failed = true; } actual.append(" "); actual.append(actualLine); actual.append("\n"); actualLine = reader.readLine(); } if (failed) { Assert.fail("expected:\n" + expected.toString() + "\nbut was:\n" + actual.toString()); } } /** * Generate change records of the requested type from the input LDIF * strings. * * @param inputLDIF * The input LDIF change records. * @return The data provider object array. * @throws Exception * If an unexpected exception occurred. */ private <T extends ChangeRecordEntry> List<Object[]> createChangeRecords( Class<T> theClass, String[] inputLDIF) throws Exception { List<Object[]> changes = new LinkedList<Object[]>(); for (String ldifString : inputLDIF) { byte[] bytes = StaticUtils.getBytes(ldifString); LDIFReader reader = new LDIFReader(new LDIFImportConfig( new ByteArrayInputStream(bytes))); ChangeRecordEntry change = reader.readChangeRecord(false); Assert.assertNotNull(change); Assert.assertTrue(theClass.isInstance(change)); String[] lines = ldifString.split("\\n"); Object[] objs = new Object[] { change, lines }; changes.add(objs); } return changes; } }