From a9b31a7fd6f665f10673721758e5dedc025135c7 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 18 Jan 2007 02:00:47 +0000
Subject: [PATCH] Update the build script to temporarily allow up to 256MB of memory for the JVM used to run the unit tests.  Also, re-enable the JMX and search filter test cases that had been disabled.

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxConnectTest.java              |   12 
 opends/build.xml                                                                                           |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java                   | 2165 +++++++++++++++++++++++++++++-----------------------------
 opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/postConnectedDisconnectTest.java |    2 
 4 files changed, 1,090 insertions(+), 1,091 deletions(-)

diff --git a/opends/build.xml b/opends/build.xml
index 9d430ef..1da3e19 100644
--- a/opends/build.xml
+++ b/opends/build.xml
@@ -923,8 +923,8 @@
       <jvmarg value="-Demma.coverage.out.merge=false" />
       <jvmarg value="-Dorg.opends.server.BuildRoot=${basedir}" />
       <jvmarg value="-Dorg.opends.test.suppressOutput=${org.opends.test.suppressOutput}" />
-      <jvmarg value="-Xmx${MEM}" />
       <jvmarg value="-Xms${MEM}" />
+      <jvmarg value="-Xmx256M" />
       <xmlfileset dir="${unittest.resource.dir}" includes="testng.xml" />
     </testng>
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxConnectTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxConnectTest.java
index 6a4870e..a0707a0 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxConnectTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxConnectTest.java
@@ -111,7 +111,7 @@
    * credentials are OK and refused when the credentials are invalid.
    *
    */
-  @Test(enabled = false, dataProvider="simpleConnect")
+  @Test(enabled = true, dataProvider="simpleConnect")
   public void simpleConnect(String user, String password,
       boolean expectedResult) throws Exception
   {
@@ -147,7 +147,7 @@
    * Test simple JMX get.
    *
    */
-  @Test(enabled = false, dataProvider="simpleGet")
+  @Test(enabled = true, dataProvider="simpleGet")
   public void simpleGet(String dn, String attributeName, Object value)
      throws Exception
   {
@@ -173,7 +173,7 @@
   /**
    * Test setting some config attribute through jmx.
    */
-  @Test(enabled = false)
+  @Test(enabled = true)
   public void simpleSet() throws Exception
   {
     OpendsJmxConnector connector = connect("cn=directory manager", "password",
@@ -208,7 +208,7 @@
    *
    * @throws Exception
    */
-  @Test(enabled = false)
+  @Test(enabled = true)
   public void disable() throws Exception
   {
     
@@ -277,7 +277,7 @@
    * Test changing JMX port through LDAP
    * @throws Exception
    */
-  @Test(enabled=false)
+  @Test(enabled=true)
   public void changePort() throws Exception
   {
     // Connect to the JMX service and get the current port
@@ -352,7 +352,7 @@
    * credentials are OK and refused when the credentials are invalid.
    *
    */
-  @Test(enabled=false)
+  @Test(enabled=true)
   public void sslConnect() throws Exception
   {
     // configure the JMX ssl key manager
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/postConnectedDisconnectTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/postConnectedDisconnectTest.java
index 0c35d8e..860dd60 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/postConnectedDisconnectTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/postConnectedDisconnectTest.java
@@ -62,7 +62,7 @@
    * Perform a simple connect.
    * @throws Exception If something wrong occurs.
    */
-  @Test(enabled = false)
+  @Test(enabled = true)
   public void checkPostconnectDisconnectPlugin() throws Exception
   {
     // Before the test, how many time postconnect and postdisconnect
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
index a7a1494..0b3d437 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
@@ -53,1089 +53,1088 @@
  * with extensible match, attribute options, and there is a lot of code
  * that is not reachable because it's in exception handling code that
  * is not exercisable externally.
- */
-// These are commented out because they make TestNG run out of memory.
+   */
 public class SearchFilterTests extends DirectoryServerTestCase {
-//
-//  @BeforeClass
-//  public void setupClass() throws Exception {
-//    TestCaseUtils.startServer();
-//  }
-//
-//  ////////////////////////////////////////////////////////////////////////////
-//  ////////////////////////////////////////////////////////////////////////////
-//  //
-//  // createFilterFromString
-//  //
-//  ////////////////////////////////////////////////////////////////////////////
-//  ////////////////////////////////////////////////////////////////////////////
-//
-//  // -------------------------------------------------------------------------
-//  //
-//  // Test valid filters.
-//  //
-//  // -------------------------------------------------------------------------
-//
-//  // These are valid filters.
-//  @DataProvider(name = "paramsCreateFilterFromStringValidFilters")
-//  public Object[][] paramsCreateFilterFromStringValidFilters() {
-//    return new Object[][]{
-//            {"(&)", "(&)"},
-//            {"(|)", "(|)"},
-//            {"(sn=test)", "(sn=test)"},
-//            {"(sn=*)", "(sn=*)"},
-//            {"(sn=)", "(sn=)"},
-//            {"(sn=*test*)", "(sn=*test*)"},
-//
-//            {"(!(sn=test))", "(!(sn=test))"},
-//            {"(|(sn=test)(sn=test2))", "(|(sn=test)(sn=test2))"},
-//
-//            {"(&(sn=test))", "(&(sn=test))"},
-//            {"(|(sn=test))", "(|(sn=test))"},
-//    };
-//  }
-//
-//  @Test(dataProvider = "paramsCreateFilterFromStringValidFilters")
-//  public void testCreateFilterFromStringValidFilters(
-//          String originalFilter,
-//          String expectedToStringFilter
-//  ) throws DirectoryException {
-//    runRecreateFilterTest(originalFilter, expectedToStringFilter);
-//  }
-//
-//  private void runRecreateFilterTest(
-//          String originalFilter,
-//          String expectedToStringFilter
-//  ) throws DirectoryException {
-//    String regenerated = SearchFilter.createFilterFromString(originalFilter).toString();
-//    Assert.assertEquals(regenerated, expectedToStringFilter, "original=" + originalFilter + ", expected=" + expectedToStringFilter);
-//  }
-//
-//  // These are valid filters.
-//  @DataProvider(name = "escapeSequenceFilters")
-//  public Object[][] escapeSequenceFilters() {
-//    final char[] CHAR_NIBBLES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-//                                 'a', 'b', 'c', 'd', 'e', 'f',
-//                                 'A', 'B', 'C', 'D', 'E', 'F'};
-//
-//    final byte[] BYTE_NIBBLES = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
-//                                 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
-//                                 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
-//
-//    List<String[]> allParameters = new ArrayList<String[]>();
-//    for (int i = 0; i < CHAR_NIBBLES.length; i++) {
-//      char highNibble = CHAR_NIBBLES[i];
-//      byte highByteNibble = BYTE_NIBBLES[i];
-//      for (int j = 0; j < CHAR_NIBBLES.length; j++) {
-//        char lowNibble = CHAR_NIBBLES[j];
-//        byte lowByteNibble = BYTE_NIBBLES[j];
-//        String inputChar = "\\" + highNibble + lowNibble;
-//        byte byteValue = (byte)((((int)highByteNibble) << 4) | lowByteNibble);
-//        String outputChar = getFilterValueForChar(byteValue);
-//
-//        // Exact match
-//        String inputFilter = "(sn=" + inputChar + ")";
-//        String outputFilter = "(sn=" + outputChar + ")";
-//        allParameters.add(new String[]{inputFilter, outputFilter});
-//
-//        // Substring
-//        inputFilter = "(sn=" + inputChar + "*" + inputChar + "*" + inputChar + ")";
-//        outputFilter = "(sn=" + outputChar + "*" + outputChar + "*" + outputChar + ")";
-//        allParameters.add(new String[]{inputFilter, outputFilter});
-//
-//        // <=
-//        inputFilter = "(sn<=" + inputChar + ")";
-//        outputFilter = "(sn<=" + outputChar + ")";
-//        allParameters.add(new String[]{inputFilter, outputFilter});
-//
-//        // >=
-//        inputFilter = "(sn>=" + inputChar + ")";
-//        outputFilter = "(sn>=" + outputChar + ")";
-//        allParameters.add(new String[]{inputFilter, outputFilter});
-//
-//        // =~
-//        inputFilter = "(sn>=" + inputChar + ")";
-//        outputFilter = "(sn>=" + outputChar + ")";
-//        allParameters.add(new String[]{inputFilter, outputFilter});
-//
-//        // =~
-//        inputFilter = "(sn:caseExactMatch:=" + inputChar + ")";
-//        outputFilter = "(sn:caseExactMatch:=" + outputChar + ")";
-//        allParameters.add(new String[]{inputFilter, outputFilter});
-//      }
-//    }
-//
-//    return (Object[][]) allParameters.toArray(new String[][]{});
-//  }
-//
-//
-//  // These are filters with invalid escape sequences.
-//  @DataProvider(name = "invalidEscapeSequenceFilters")
-//  public Object[][] invalidEscapeSequenceFilters() {
-//    final char[] VALID_NIBBLES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-//                                 'a', 'b', 'c', 'd', 'e', 'f',
-//                                 'A', 'B', 'C', 'D', 'E', 'F'};
-//
-//    final char[] INVALID_NIBBBLES = {'g', 'z', 'G', 'Z', '-', '=', '+', '\00', ')',
-//                                     'n', 't', '\\'};
-//
-//    List<String> invalidEscapeSequences = new ArrayList<String>();
-//
-//    for (int i = 0; i < VALID_NIBBLES.length; i++) {
-//      char validNibble = VALID_NIBBLES[i];
-//      for (int j = 0; j < INVALID_NIBBBLES.length; j++) {
-//        char invalidNibble = INVALID_NIBBBLES[j];
-//
-//        invalidEscapeSequences.add("\\" + validNibble + invalidNibble);
-//        invalidEscapeSequences.add("\\" + invalidNibble + validNibble);
-//      }
-//      // Also do a test case where we only have one character in the escape sequence.
-//      invalidEscapeSequences.add("\\" + validNibble);
-//    }
-//
-//    List<String[]> allParameters = new ArrayList<String[]>();
-//    for (String invalidEscape : invalidEscapeSequences) {
-//      // Exact match
-//      allParameters.add(new String[]{"(sn=" + invalidEscape + ")"});
-//      allParameters.add(new String[]{"(sn=" + invalidEscape});
-//
-//      // Substring
-//      allParameters.add(new String[]{"(sn=" + invalidEscape + "*" + invalidEscape + "*" + invalidEscape + ")"});
-//      allParameters.add(new String[]{"(sn=" + invalidEscape + "*" + invalidEscape + "*" + invalidEscape});
-//
-//      // <=
-//      allParameters.add(new String[]{"(sn<=" + invalidEscape + ")"});
-//      allParameters.add(new String[]{"(sn<=" + invalidEscape});
-//
-//      // >=
-//      allParameters.add(new String[]{"(sn>=" + invalidEscape + ")"});
-//      allParameters.add(new String[]{"(sn>=" + invalidEscape});
-//
-//      // =~
-//      allParameters.add(new String[]{"(sn>=" + invalidEscape + ")"});
-//      allParameters.add(new String[]{"(sn>=" + invalidEscape});
-//
-//      // =~
-//      allParameters.add(new String[]{"(sn:caseExactMatch:=" + invalidEscape + ")"});
-//      allParameters.add(new String[]{"(sn:caseExactMatch:=" + invalidEscape});
-//    }
-//
-//    return (Object[][]) allParameters.toArray(new String[][]{});
-//  }
-//
-//
-//  /**
-//   * @return a value that can be used in an LDAP filter.
-//   */
-//  private String getFilterValueForChar(byte value) {
-//    if (((value & 0x7F) != value) ||  // Not 7-bit clean
-//         (value <= 0x1F) ||           // Below the printable character range
-//         (value == 0x28) ||           // Open parenthesis
-//         (value == 0x29) ||           // Close parenthesis
-//         (value == 0x2A) ||           // Asterisk
-//         (value == 0x5C) ||           // Backslash
-//         (value == 0x7F))             // Delete character
-//    {
-//      return "\\" + StaticUtils.byteToHex(value);
-//    } else {
-//      return "" + ((char)value);
-//    }
-//  }
-//
-//  @Test(dataProvider = "escapeSequenceFilters")
-//  public void testRecreateFilterWithEscape(
-//          String originalFilter,
-//          String expectedToStringFilter
-//  ) throws DirectoryException {
-//    runRecreateFilterTest(originalFilter, expectedToStringFilter);
-//  }
-//
-//  @Test(dataProvider = "invalidEscapeSequenceFilters",
-//        expectedExceptions = DirectoryException.class)
-//  public void testFilterWithInvalidEscape(
-//          String filterWithInvalidEscape)
-//          throws DirectoryException {
-//    // This should fail with a parse error.
-//    SearchFilter.createFilterFromString(filterWithInvalidEscape);
-//  }
-//
-//
-//  // -------------------------------------------------------------------------
-//  //
-//  // Test invalid filters.
-//  //
-//  // -------------------------------------------------------------------------
-//
-//  //
-//  // Invalid filters that are detected.
-//  //
-//
-//  @DataProvider(name = "invalidFilters")
-//  public Object[][] invalidFilters() {
-//    return new Object[][]{
-//            {null},
-//            {"(cn)"},
-//            {"()"},
-//            {"("},
-//            {"(&(sn=test)"},
-//            {"(|(sn=test)"},
-//            {"(!(sn=test)"},
-//            {"(&(sn=test)))"},
-//            {"(|(sn=test)))"},
-//            // TODO: open a bug for this.
-////            {"(!(sn=test)))"},
-//            {"(sn=\\A)"},
-//            {"(sn=\\1H)"},
-//            {"(sn=\\H1)"},
-//    };
-//  }
-//
-//  @Test(dataProvider = "invalidFilters",
-//        expectedExceptions = DirectoryException.class)
-//  public void testCreateFilterFromStringInvalidFilters(String invalidFilter)
-//          throws DirectoryException {
-//    SearchFilter.createFilterFromString(invalidFilter).toString();
-//  }
-//
-//  //
-//  // This is more or less the same as what's above, but it's for invalid
-//  // filters that are not currently detected by the parser.  To turn these
-//  // on, remove them from the broken group.  As the code is modified to handle
-//  // these cases, please add these test cases to the
-//  // paramsCreateFilterFromStringInvalidFilters DataProvider.
-//  //
-//
-//  @DataProvider(name = "uncaughtInvalidFilters")
-//  public Object[][] paramsCreateFilterFromStringUncaughtInvalidFilters() {
-//    return new Object[][]{
-//            {"(cn=**)"},
-//            {"( sn = test )"},
-//            {"&(cn=*)"},
-//            {"(!(sn=test)(sn=test2))"},
-//            {"(objectclass=**)"},
-//    };
-//  }
-//
-//  @Test(dataProvider = "uncaughtInvalidFilters",
-//        expectedExceptions = DirectoryException.class,
-//        // FIXME:  These currently aren't detected
-//        enabled = false)
-//  public void testCreateFilterFromStringUncaughtInvalidFilters(String invalidFilter)
-//          throws DirectoryException {
-//    SearchFilter.createFilterFromString(invalidFilter).toString();
-//  }
-//
-//
-//  ////////////////////////////////////////////////////////////////////////////
-//  ////////////////////////////////////////////////////////////////////////////
-//  //
-//  // matches
-//  //
-//  ////////////////////////////////////////////////////////////////////////////
-//  ////////////////////////////////////////////////////////////////////////////
-//
-//  private static final String JOHN_SMITH_LDIF = TestCaseUtils.makeLdif(
-//          "dn: cn=John Smith,dc=example,dc=com",
-//          "objectclass: inetorgperson",
-//          "cn: John Smith",
-//          "cn;lang-en: Jonathan Smith",
-//          "sn: Smith",
-//          "givenname: John",
-//          "internationaliSDNNumber: 12345",
-//          "displayName: *",
-//          "title: tattoos",
-//          "labeledUri: http://opends.org/john"
-//          );
-//
-//  @DataProvider(name = "matchesParams")
-//  public Object[][] matchesParams() {
-//    return new Object[][]{
-//            {JOHN_SMITH_LDIF, "(objectclass=inetorgperson)", true},
-//            {JOHN_SMITH_LDIF, "(objectclass=iNetOrgPeRsOn)", true},
-//            {JOHN_SMITH_LDIF, "(objectclass=*)", true},
-//            {JOHN_SMITH_LDIF, "(objectclass=person)", false},
-//
-//            {JOHN_SMITH_LDIF, "(cn=John Smith)", true},
-//            {JOHN_SMITH_LDIF, "(cn=Jonathan Smith)", true},
-//            {JOHN_SMITH_LDIF, "(cn=JOHN SmITh)", true},
-//            {JOHN_SMITH_LDIF, "(cn=*)", true},
-//            {JOHN_SMITH_LDIF, "(cn=*John Smith*)", true},
-//            {JOHN_SMITH_LDIF, "(cn=*Jo*ith*)", true},
-//            {JOHN_SMITH_LDIF, "(cn=*Jo*i*th*)", true},
-//            {JOHN_SMITH_LDIF, "(cn=*Joh*ohn*)", false},  // this shouldn't match
-//            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*23*34*)", false},  // this shouldn't match
-//
-//            {JOHN_SMITH_LDIF, "(cn=*o*n*)", true},
-//            {JOHN_SMITH_LDIF, "(cn=*n*o*)", false},
-//
-//            // attribute options
-//            {JOHN_SMITH_LDIF, "(cn;lang-en=Jonathan Smith)", true},
-//            {JOHN_SMITH_LDIF, "(cn;lang-en=Jonathan Smithe)", false},
-//            {JOHN_SMITH_LDIF, "(cn;lang-fr=Jonathan Smith)", false},
-//            {JOHN_SMITH_LDIF, "(cn;lang-en=*jon*an*)", true},
-//
-//            // attribute subtypes.  Enable this once 593 is fixed.
-////            {JOHN_SMITH_LDIF, "(name=John Smith)", true},
-////            {JOHN_SMITH_LDIF, "(name=*Smith*)", true},
-////            {JOHN_SMITH_LDIF, "(name;lang-en=Jonathan Smith)", true},  // ? maybe not
-////            {JOHN_SMITH_LDIF, "(name;lang-en=*Jonathan*)", true},  // ? maybe not
-//
-//            // Enable this once
-////            {JOHN_SMITH_LDIF, "(cn=*Jo**i*th*)", true},
-//
-//            {JOHN_SMITH_LDIF, "(cn=\\4Aohn*)", true}, // \4A = J
-//            {JOHN_SMITH_LDIF, "(|(cn=Jane Smith)(cn=John Smith))", true},
-//
-//            {JOHN_SMITH_LDIF, "(title~=tattoos)", true},
-//            {JOHN_SMITH_LDIF, "(title~=tattos)", true},
-//
-//            {JOHN_SMITH_LDIF, "(labeledUri=http://opends.org/john)", true},
-//            {JOHN_SMITH_LDIF, "(labeledUri=http://opends.org/JOHN)", false},
-//            {JOHN_SMITH_LDIF, "(labeledUri=http://*/john)", true},
-//            {JOHN_SMITH_LDIF, "(labeledUri=http://*/JOHN)", false},
-//
-//            {JOHN_SMITH_LDIF, "(cn>=John Smith)", true},
-//            {JOHN_SMITH_LDIF, "(cn>=J)", true},
-//            {JOHN_SMITH_LDIF, "(cn<=J)", false},
-//
-//            {JOHN_SMITH_LDIF, "(cn=Jane Smith)", false},
-//
-//            {JOHN_SMITH_LDIF, "(displayName=\\2A)", true}, // \2A = *
-//
-//            // 2.5.4.4 is Smith
-//            {JOHN_SMITH_LDIF, "(2.5.4.4=Smith)", true},
-//
-//            {JOHN_SMITH_LDIF, "(sn:caseExactMatch:=Smith)", true},
-//            {JOHN_SMITH_LDIF, "(sn:caseExactMatch:=smith)", false},
-//
-//            // Test cases for 730
-//            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*12*45*)", true},
-//            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*45*12*)", false},
-//
-//            // TODO: open a bug for all of these.
-////            {JOHN_SMITH_LDIF, "(:caseExactMatch:=Smith)", true},
-////            {JOHN_SMITH_LDIF, "(:caseExactMatch:=NotSmith)", false},
-//
-//            // Look at 4515 for some more examples.  Ask Neil.
-////            {JOHN_SMITH_LDIF, "(:dn:caseExactMatch:=example)", true},
-////            {JOHN_SMITH_LDIF, "(:dn:caseExactMatch:=notexample)", false},
-//    };
-//  }
-//
-//  @Test(dataProvider = "matchesParams")
-//  public void testMatches(String ldifEntry, String filterStr, boolean expectMatch) throws Exception {
-//    runMatchTest(ldifEntry, filterStr, expectMatch);
-//  }
-//
-//  private void runMatchTest(String ldifEntry, String filterStr, boolean expectMatch) throws Exception {
-//    Entry entry = TestCaseUtils.entryFromLdifString(ldifEntry);
-//
-//    runSingleMatchTest(entry, filterStr, expectMatch);
-//    runSingleMatchTest(entry, "(|" + filterStr + ")", expectMatch);
-//    runSingleMatchTest(entry, "(&" + filterStr + ")", expectMatch);
-//    runSingleMatchTest(entry, "(!" + filterStr + ")", !expectMatch);
-//  }
-//
-//  private void runSingleMatchTest(Entry entry, String filterStr, boolean expectMatch) throws Exception {
-//    final SearchFilter filter = SearchFilter.createFilterFromString(filterStr);
-//    boolean matches = filter.matchesEntry(entry);
-//    Assert.assertEquals(matches, expectMatch, "Filter=" + filter + "\nEntry=" + entry);
-//  }
-//
-//  ////////////////////////////////////////////////////////////////////////////
-//  ////////////////////////////////////////////////////////////////////////////
-//  //
-//  // Filter construction
-//  //
-//  ////////////////////////////////////////////////////////////////////////////
-//  ////////////////////////////////////////////////////////////////////////////
-//
-//
-//  /**
-//   *
-//   */
-//  private static final String makeSimpleLdif(String givenname, String sn) {
-//    String cn = givenname + " " + sn;
-//    return TestCaseUtils.makeLdif(
-//          "dn: cn=" + cn + ",dc=example,dc=com",
-//          "objectclass: inetorgperson",
-//          "cn: " + cn,
-//          "sn: " + sn,
-//          "givenname: " + givenname
-//          );
-//  }
-//
-//  private static final String JANE_SMITH_LDIF = makeSimpleLdif("Jane", "Smith");
-//  private static final String JANE_AUSTIN_LDIF = makeSimpleLdif("Jane", "Austin");
-//  private static final String JOE_SMITH_LDIF = makeSimpleLdif("Joe", "Smith");
-//  private static final String JOE_AUSTIN_LDIF = makeSimpleLdif("Joe", "Austin");
-//
-//  private static final List<String> ALL_ENTRIES_LDIF =
-//          Collections.unmodifiableList(asList(JANE_SMITH_LDIF,
-//                                              JANE_AUSTIN_LDIF,
-//                                              JOE_SMITH_LDIF,
-//                                              JOE_AUSTIN_LDIF));
-//
-//
-//  /**
-//   *
-//   */
-//  private List<String> getEntriesExcluding(List<String> matchedEntries) {
-//    List<String> unmatched = new ArrayList<String>(ALL_ENTRIES_LDIF);
-//    unmatched.removeAll(matchedEntries);
-//    return unmatched;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private static class FilterDescription {
-//    private SearchFilter searchFilter;
-//
-//    private List<String> matchedEntriesLdif;
-//    private List<String> unmatchedEntriesLdif;
-//
-//    private FilterType filterType;
-//    private LinkedHashSet<SearchFilter> filterComponents = new LinkedHashSet<SearchFilter>();
-//    private SearchFilter notComponent;
-//    private AttributeValue assertionValue;
-//    private AttributeType attributeType;
-//    private ByteString subInitialElement;
-//    private List<ByteString> subAnyElements = new ArrayList<ByteString>();
-//    private ByteString subFinalElement;
-//    private String matchingRuleId;
-//    private boolean dnAttributes;
-//
-//
-//    /**
-//     *
-//     */
-//    public void validateFilterFields() throws AssertionError {
-//      if (!searchFilter.getFilterType().equals(filterType)) {
-//        throwUnequalError("filterTypes");
-//      }
-//
-//      if (!searchFilter.getFilterComponents().equals(filterComponents)) {
-//        throwUnequalError("filterComponents");
-//      }
-//
-//      if (!objectsAreEqual(searchFilter.getNotComponent(), notComponent)) {
-//        throwUnequalError("notComponent");
-//      }
-//
-//      if (!objectsAreEqual(searchFilter.getAssertionValue(), assertionValue)) {
-//        throwUnequalError("assertionValue");
-//      }
-//
-//      if (!objectsAreEqual(searchFilter.getAttributeType(), attributeType)) {
-//        throwUnequalError("attributeType");
-//      }
-//
-//      if (!objectsAreEqual(searchFilter.getSubInitialElement(), subInitialElement)) {
-//        throwUnequalError("subInitial");
-//      }
-//
-//      if (!objectsAreEqual(searchFilter.getSubAnyElements(), subAnyElements)) {
-//        throwUnequalError("subAny");
-//      }
-//
-//      if (!objectsAreEqual(searchFilter.getSubFinalElement(), subFinalElement)) {
-//        throwUnequalError("subFinal");
-//      }
-//
-//      if (!objectsAreEqual(searchFilter.getMatchingRuleID(), matchingRuleId)) {
-//        throwUnequalError("matchingRuleId");
-//      }
-//
-//      if (searchFilter.getDNAttributes() != dnAttributes) {
-//        throwUnequalError("dnAttributes");
-//      }
-//    }
-//
-//
-//    /**
-//     *
-//     */
-//    private void throwUnequalError(String message) throws AssertionError {
-//      throw new AssertionError("Filter differs from what is expected '" + message + "' differ.\n" + toString());
-//    }
-//
-//
-//    /**
-//     *
-//     */
-//    @Override
-//    public String toString() {
-//      return "FilterDescription: \n" +
-//              "\tsearchFilter=" + searchFilter + "\n" +
-//              "\tfilterType = " + filterType + "\n" +
-//              "\tfilterComponents = " + filterComponents + "\n" +
-//              "\tnotComponent = " + notComponent + "\n" +
-//              "\tassertionValue = " + assertionValue + "\n" +
-//              "\tattributeType = " + attributeType + "\n" +
-//              "\tsubInitialElement = " + subInitialElement + "\n" +
-//              "\tsubAnyElements = " + subAnyElements + "\n" +
-//              "\tsubFinalElement = " + subFinalElement + "\n" +
-//              "\tmatchingRuleId = " + dnAttributes + "\n";
-//    }
-//
-//
-//    /**
-//     *
-//     */
-//    private FilterDescription negate() {
-//      FilterDescription negation = new FilterDescription();
-//      negation.searchFilter = SearchFilter.createNOTFilter(searchFilter);
-//
-//      // Flip-flop these
-//      negation.matchedEntriesLdif = unmatchedEntriesLdif;
-//      negation.unmatchedEntriesLdif = matchedEntriesLdif;
-//
-//      negation.filterType = FilterType.NOT;
-//      negation.notComponent = searchFilter;
-//
-//      return negation;
-//    }
-//
-//
-//    /**
-//     *
-//     */
-//    public FilterDescription clone() {
-//      FilterDescription that = new FilterDescription();
-//
-//      that.searchFilter = this.searchFilter;
-//      that.matchedEntriesLdif = this.matchedEntriesLdif;
-//      that.unmatchedEntriesLdif = this.unmatchedEntriesLdif;
-//      that.filterType = this.filterType;
-//      that.filterComponents = this.filterComponents;
-//      that.notComponent = this.notComponent;
-//      that.assertionValue = this.assertionValue;
-//      that.attributeType = this.attributeType;
-//      that.subInitialElement = this.subInitialElement;
-//      that.subAnyElements = this.subAnyElements;
-//      that.subFinalElement = this.subFinalElement;
-//      that.matchingRuleId = this.matchingRuleId;
-//      that.dnAttributes = this.dnAttributes;
-//
-//      return that;
-//    }
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private FilterDescription assertionFilterDescription(FilterType filterType,
-//                                                       String attributeType,
-//                                                       String attributeValue,
-//                                                       List<String> matchedEntries) {
-//    FilterDescription description = new FilterDescription();
-//
-//    description.filterType = filterType;
-//    description.attributeType = DirectoryServer.getAttributeType(attributeType);
-//    description.assertionValue = new AttributeValue(description.attributeType, attributeValue);
-//
-//    if (filterType == FilterType.EQUALITY) {
-//      description.searchFilter = SearchFilter.createEqualityFilter(description.attributeType,
-//                                                                   description.assertionValue);
-//    } else if (filterType == FilterType.LESS_OR_EQUAL) {
-//      description.searchFilter = SearchFilter.createLessOrEqualFilter(description.attributeType,
-//                                                                      description.assertionValue);
-//    } else if (filterType == FilterType.GREATER_OR_EQUAL) {
-//      description.searchFilter = SearchFilter.createGreaterOrEqualFilter(description.attributeType,
-//                                                                         description.assertionValue);
-//    } else if (filterType == FilterType.APPROXIMATE_MATCH) {
-//      description.searchFilter = SearchFilter.createApproximateFilter(description.attributeType,
-//                                                                      description.assertionValue);
-//    } else {
-//      fail(filterType + " is not handled.");
-//    }
-//
-//    description.matchedEntriesLdif = matchedEntries;
-//    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
-//
-//    return description;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private FilterDescription equalityFilterDescription(String attributeType,
-//                                                      String attributeValue,
-//                                                      List<String> matchedEntries) {
-//    return assertionFilterDescription(FilterType.EQUALITY, attributeType, attributeValue, matchedEntries);
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private FilterDescription lessEqualFilterDescription(String attributeType,
-//                                                       String attributeValue,
-//                                                       List<String> matchedEntries) {
-//    return assertionFilterDescription(FilterType.LESS_OR_EQUAL, attributeType, attributeValue, matchedEntries);
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private FilterDescription greaterEqualFilterDescription(String attributeType,
-//                                                          String attributeValue,
-//                                                          List<String> matchedEntries) {
-//    return assertionFilterDescription(FilterType.GREATER_OR_EQUAL, attributeType, attributeValue, matchedEntries);
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private FilterDescription approximateFilterDescription(String attributeType,
-//                                                         String attributeValue,
-//                                                         List<String> matchedEntries) {
-//    return assertionFilterDescription(FilterType.APPROXIMATE_MATCH, attributeType, attributeValue, matchedEntries);
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private FilterDescription substringFilterDescription(String attributeType,
-//                                                       String subInitial,
-//                                                       List<String> subAny,
-//                                                       String subFinal,
-//                                                       List<String> matchedEntries) {
-//    FilterDescription description = new FilterDescription();
-//
-//    description.filterType = FilterType.SUBSTRING;
-//    description.attributeType = DirectoryServer.getAttributeType(attributeType);
-//
-//    description.subInitialElement = new ASN1OctetString(subInitial);
-//    description.subAnyElements = new ArrayList<ByteString>();
-//    for (int i = 0; (subAny != null) && (i < subAny.size()); i++) {
-//      String s = subAny.get(i);
-//      description.subAnyElements.add(new ASN1OctetString(s));
-//    }
-//    description.subFinalElement = new ASN1OctetString(subFinal);
-//
-//    description.searchFilter = SearchFilter.createSubstringFilter(description.attributeType,
-//            description.subInitialElement,
-//            description.subAnyElements,
-//            description.subFinalElement);
-//
-//
-//    description.matchedEntriesLdif = matchedEntries;
-//    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
-//
-//    return description;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private List<FilterDescription> getNotFilters(List<FilterDescription> filters) {
-//    List<FilterDescription> notFilters = new ArrayList<FilterDescription>();
-//
-//    for (FilterDescription filter: filters) {
-//      notFilters.add(filter.negate());
-//    }
-//
-//    return notFilters;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private FilterDescription getAndFilter(List<FilterDescription> filters) {
-//    FilterDescription andFilter = new FilterDescription();
-//
-//    List<String> matchedEntries = new ArrayList<String>(ALL_ENTRIES_LDIF);
-//    List<SearchFilter> filterComponents = new ArrayList<SearchFilter>();
-//
-//    for (FilterDescription filter: filters) {
-//      matchedEntries.retainAll(filter.matchedEntriesLdif);
-//      filterComponents.add(filter.searchFilter);
-//    }
-//
-//    andFilter.searchFilter = SearchFilter.createANDFilter(filterComponents);
-//    andFilter.filterComponents = new LinkedHashSet<SearchFilter>(filterComponents);
-//
-//    andFilter.filterType = FilterType.AND;
-//
-//    andFilter.matchedEntriesLdif = matchedEntries;
-//    andFilter.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
-//
-//    return andFilter;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private List<FilterDescription> getAndFilters(List<FilterDescription> filters) {
-//    List<FilterDescription> andFilters = new ArrayList<FilterDescription>();
-//
-//    for (FilterDescription first: filters) {
-//      for (FilterDescription second: filters) {
-//        andFilters.add(getAndFilter(asList(first, second)));
-//      }
-//    }
-//
-//    return andFilters;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private FilterDescription getOrFilter(List<FilterDescription> filters) {
-//    FilterDescription orFilter = new FilterDescription();
-//
-//    List<String> unmatchedEntries = new ArrayList<String>(ALL_ENTRIES_LDIF);
-//    List<SearchFilter> filterComponents = new ArrayList<SearchFilter>();
-//
-//    for (FilterDescription filter: filters) {
-//      unmatchedEntries.retainAll(filter.unmatchedEntriesLdif);
-//      filterComponents.add(filter.searchFilter);
-//    }
-//
-//    orFilter.searchFilter = SearchFilter.createORFilter(filterComponents);
-//    orFilter.filterComponents = new LinkedHashSet<SearchFilter>(filterComponents);
-//
-//    orFilter.filterType = FilterType.OR;
-//
-//    // Since we're not using Sets, we've whittled down unmatched entries from
-//    // the full set instead of adding to matchedEntries, which would lead
-//    // to duplicates.
-//    orFilter.unmatchedEntriesLdif = unmatchedEntries;
-//    orFilter.matchedEntriesLdif = getEntriesExcluding(unmatchedEntries);
-//
-//    return orFilter;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private List<FilterDescription> getOrFilters(List<FilterDescription> filters) {
-//    List<FilterDescription> orFilters = new ArrayList<FilterDescription>();
-//
-//    for (FilterDescription first: filters) {
-//      for (FilterDescription second: filters) {
-//        orFilters.add(getOrFilter(asList(first, second)));
-//      }
-//    }
-//
-//    return orFilters;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private List<FilterDescription> getEqualityFilters() throws Exception {
-//    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
-//
-//    descriptions.add(equalityFilterDescription("sn", "Smith",
-//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
-//
-//    descriptions.add(equalityFilterDescription("givenname", "Jane",
-//            asList(JANE_SMITH_LDIF, JANE_AUSTIN_LDIF)));
-//
-//    return descriptions;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private List<FilterDescription> getApproximateFilters() throws Exception {
-//    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
-//
-//    descriptions.add(approximateFilterDescription("sn", "Smythe",
-//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
-//
-//    return descriptions;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private List<FilterDescription> getSubstringFilters() throws Exception {
-//    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
-//
-//    descriptions.add(substringFilterDescription(
-//            "sn",
-//            "S", asList("i"), "th", // S*i*th
-//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
-//
-//    return descriptions;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  private List<FilterDescription> getInequalityFilters() throws Exception {
-//    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
-//
-//    descriptions.add(lessEqualFilterDescription("sn", "Aus",
-//            (List<String>)(new ArrayList<String>())));
-//
-//    descriptions.add(greaterEqualFilterDescription("sn", "Aus",
-//            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
-//                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
-//
-//
-//    descriptions.add(lessEqualFilterDescription("sn", "Smi",
-//            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF)));
-//
-//    descriptions.add(greaterEqualFilterDescription("sn", "Smi",
-//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
-//
-//
-//    descriptions.add(lessEqualFilterDescription("sn", "Smith",
-//            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
-//                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
-//
-//    descriptions.add(greaterEqualFilterDescription("sn", "Smith",
-//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
-//
-//
-//    return descriptions;
-//  }
-//
-//
-//  /**
-//   * Updates to this should also be made in getMinimalFilterDescriptionList.
-//   * @see #getMinimalFilterDescriptionList
-//   */
-//  private List<FilterDescription> getFilterDescriptionList() throws Exception {
-//    List<FilterDescription> baseDescriptions = new ArrayList<FilterDescription>();
-//
-//    baseDescriptions.addAll(getEqualityFilters());
-//    baseDescriptions.addAll(getInequalityFilters());
-//    baseDescriptions.addAll(getApproximateFilters());
-//    baseDescriptions.addAll(getSubstringFilters());
-//    baseDescriptions.addAll(getNotFilters(baseDescriptions));
-//
-//    List<FilterDescription> allDescriptions = new ArrayList<FilterDescription>();
-//
-//    allDescriptions.addAll(getAndFilters(baseDescriptions));
-//    allDescriptions.addAll(getOrFilters(baseDescriptions));
-//    allDescriptions.addAll(baseDescriptions);
-//
-//    return allDescriptions;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  public List<FilterDescription> getMinimalFilterDescriptionList() throws Exception {
-//    List<FilterDescription> baseDescriptions = new ArrayList<FilterDescription>();
-//    List<FilterDescription> allDescriptions = new ArrayList<FilterDescription>();
-//
-//    baseDescriptions.addAll(getEqualityFilters().subList(0, 1));
-//    baseDescriptions.addAll(getInequalityFilters().subList(0, 2));
-//    baseDescriptions.addAll(getSubstringFilters().subList(0, 1));
-//    baseDescriptions.addAll(getNotFilters(baseDescriptions).subList(0, 1));
-//
-//    allDescriptions.addAll(baseDescriptions);
-//    allDescriptions.addAll(getAndFilters(baseDescriptions).subList(0, 2));
-//    allDescriptions.addAll(getOrFilters(baseDescriptions).subList(0, 2));
-//
-//    return allDescriptions;
-//  }
-//
-//
-//
-//  /**
-//   *
-//   */
-//  @DataProvider(name = "filterDescriptions")
-//  public Object[][] getFilterDescriptions() throws Exception {
-//    List<FilterDescription> allDescriptions = getFilterDescriptionList();
-//
-//    // Now convert to [][]
-//    FilterDescription[][] descriptionArray = new FilterDescription[allDescriptions.size()][];
-//    for (int i = 0; i < allDescriptions.size(); i++) {
-//      FilterDescription description = allDescriptions.get(i);
-//      descriptionArray[i] = new FilterDescription[]{description};
-//    }
-//
-//    return descriptionArray;
-//  }
-//
-//
-//  @Test(dataProvider = "filterDescriptions")
-//  public void testFilterConstruction(FilterDescription description) throws Exception {
-//    description.validateFilterFields();
-//
-//    for (String ldif: description.matchedEntriesLdif) {
-//      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
-//      if (!description.searchFilter.matchesEntry(entry)) {
-//        fail("Expected to match entry. " + description + entry);
-//      }
-//    }
-//
-//    for (String ldif: description.unmatchedEntriesLdif) {
-//      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
-//      if (description.searchFilter.matchesEntry(entry)) {
-//        fail("Should not have matched entry. " + description + entry);
-//      }
-//    }
-//  }
-//
-//  // TODO: test more on extensible match and attribute options
-//  // TODO: test that we fail when creating filters without specifying all of the parameters
-//  // TODO: we need to test attribute options!
-//  // TODO: test the audio attribute since it's octetStringMatch
-//  // TODO: test the homePhone attribute since   EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstringsMatch
-//  // TODO: test labeledURI since it's  caseExactMatch SUBSTR caseExactSubstringsMatch
-//  // TODO: test mail since it's EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch
-//  // TODO: test secretary since it's distinguishedNameMatch
-//  // TODO: test x500UniqueIdentifier since it's bitStringMatch
-//
-//
-//  private static final Object[][] TEST_EQUALS_PARAMS = new Object[][]{
-//          // These have duplicates, and their String representation should even reflect that.
-//          {"(&(sn=Smith))", "(&(sn=Smith)(sn=Smith))", true, true},
-//          {"(|(sn=Smith))", "(|(sn=Smith)(sn=Smith))", true, true},
-//
-//          // These are reordered, so they are equivalent, but their String representations will differ
-//          {"(&(sn=Smith)(sn<=Aus))", "(&(sn<=Aus)(sn=Smith))", true, false},
-//          {"(|(sn=Smith)(sn<=Aus))", "(|(sn<=Aus)(sn=Smith))", true, false},
-//
-//          // These should be case insensitive
-//          {"(SN=Smith)", "(sn=Smith)", true, true},
-//          {"(sn=smith)", "(sn=Smith)", true, false},
-//          {"(SN=S*th)", "(sn=S*th)", true, true},
-//
-//          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=Smith)", true, true},
-//
-//          // This demonstrates bug 704.
-////          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=smith)", false, false},
-//
-//          // TODO: open a bug for this.
-////          {"(:dn:caseExactMatch:=example)", "(:DN:caseExactMatch:=example)", true, true}, // ? String not match
-//
-//          // 2.5.4.4 is 'sn'
-//          {"(2.5.4.4=Smith)", "(2.5.4.4=Smith)", true, true},
-//          {"(2.5.4.4=Smith)", "(sn=Smith)", true, true},
-//
-//          {"(sn;lang-en=Smith)", "(sn;lang-en=Smith)", true, true},
-//
-//          // This demonstrates bug 706
-////          {"(sn;lang-en=Smith)", "(sn=Smith)", false, false},
-//
-//
-//          // This demonstrates bug 705.
-////          {"(sn=s*t*h)", "(sn=S*T*H)", true, false},
-//
-//          // These should be case sensitive
-//          {"(labeledURI=http://opends.org)", "(labeledURI=http://OpenDS.org)", false, false},
-//          {"(labeledURI=http://opends*)", "(labeledURI=http://OpenDS*)", false, false},
-//
-//          // These are WYSIWIG
-//          {"(sn=*)", "(sn=*)", true, true},
-//          {"(sn=S*)", "(sn=S*th)", false, false},
-//          {"(sn=*S)", "(sn=S*th)", false, false},
-//          {"(sn=S*t)", "(sn=S*th)", false, false},
-//          {"(sn=*i*t*)", "(sn=*i*t*)", true, true},
-//          {"(sn=*t*i*)", "(sn=*i*t*)", false, false},  // Test case for 695
-//          {"(sn=S*i*t)", "(sn=S*th)", false, false},
-//          {"(sn=Smith)", "(sn=Smith)", true, true},
-//          {"(sn=Smith)", "(sn<=Aus)", false, false},
-//          {"(sn=Smith)", "(sn>=Aus)", false, false},
-//          {"(sn=Smith)", "(sn=S*i*th)", false, false},
-//          {"(sn=Smith)", "(!(sn=Smith))", false, false},
-//          {"(sn=Smith)", "(&(sn=Smith)(sn<=Aus))", false, false},
-//          {"(sn=Smith)", "(|(sn=Smith)(sn<=Aus))", false, false},
-//          {"(sn<=Aus)", "(sn<=Aus)", true, true},
-//          {"(sn<=Aus)", "(sn>=Aus)", false, false},
-//          {"(sn<=Aus)", "(sn=S*i*th)", false, false},
-//          {"(sn<=Aus)", "(!(sn=Smith))", false, false},
-//          {"(sn<=Aus)", "(&(sn=Smith)(sn=Smith))", false, false},
-//          {"(sn<=Aus)", "(&(sn=Smith)(sn<=Aus))", false, false},
-//          {"(sn<=Aus)", "(|(sn=Smith)(sn=Smith))", false, false},
-//          {"(sn<=Aus)", "(|(sn=Smith)(sn<=Aus))", false, false},
-//          {"(sn>=Aus)", "(sn>=Aus)", true, true},
-//          {"(sn>=Aus)", "(sn=S*i*th)", false, false},
-//          {"(sn>=Aus)", "(!(sn=Smith))", false, false},
-//          {"(sn>=Aus)", "(&(sn=Smith)(sn=Smith))", false, false},
-//          {"(sn>=Aus)", "(&(sn=Smith)(sn<=Aus))", false, false},
-//          {"(sn>=Aus)", "(|(sn=Smith)(sn=Smith))", false, false},
-//          {"(sn>=Aus)", "(|(sn=Smith)(sn<=Aus))", false, false},
-//          {"(sn=S*i*th)", "(sn=S*i*th)", true, true},
-//          {"(sn=S*i*th)", "(!(sn=Smith))", false, false},
-//          {"(sn=S*i*th)", "(&(sn=Smith)(sn=Smith))", false, false},
-//          {"(sn=S*i*th)", "(&(sn=Smith)(sn<=Aus))", false, false},
-//          {"(sn=S*i*th)", "(|(sn=Smith)(sn=Smith))", false, false},
-//          {"(sn=S*i*th)", "(|(sn=Smith)(sn<=Aus))", false, false},
-//          {"(!(sn=Smith))", "(!(sn=Smith))", true, true},
-//          {"(!(sn=Smith))", "(&(sn=Smith)(sn=Smith))", false, false},
-//          {"(!(sn=Smith))", "(&(sn=Smith)(sn<=Aus))", false, false},
-//          {"(!(sn=Smith))", "(|(sn=Smith)(sn=Smith))", false, false},
-//          {"(!(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
-//          {"(&(sn=Smith)(sn=Smith))", "(&(sn=Smith)(sn=Smith))", true, true},
-//          {"(&(sn=Smith)(sn=Smith))", "(&(sn=Smith)(sn<=Aus))", false, false},
-//          {"(&(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn=Smith))", false, false},
-//          {"(&(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
-//          {"(&(sn=Smith)(sn<=Aus))", "(&(sn=Smith)(sn<=Aus))", true, true},
-//          {"(&(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn=Smith))", false, false},
-//          {"(&(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn<=Aus))", false, false},
-//          {"(|(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn=Smith))", true, true},
-//          {"(|(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
-//          {"(|(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn<=Aus))", true, true},
-//          {"(&(sn=Smith)(sn<=Aus))", "(&(sn=Smith)(sn>=Aus))", false, false},
-//          {"(|(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn>=Aus))", false, false},
-//
-//
-//  };
-//
-//
-//  /**
-//   *
-//   */
-//  @DataProvider(name = "equalsTest")
-//  public Object[][] getEqualsTests() throws Exception {
-//    return TEST_EQUALS_PARAMS;
-//  }
-//
-//
-//  /**
-//   *
-//   */
-//  @Test(dataProvider = "equalsTest")
-//  public void testEquals(String stringFilter1, String stringFilter2, boolean expectEquals, boolean expectStringEquals) throws Exception {
-//    SearchFilter filter1 = SearchFilter.createFilterFromString(stringFilter1);
-//    SearchFilter filter2 = SearchFilter.createFilterFromString(stringFilter2);
-//
-//    boolean actualEquals = filter1.equals(filter2);
-//    assertEquals(actualEquals, expectEquals,
-//                 "Expected " + filter1 + (expectEquals ? " == " : " != ") + filter2);
-//
-//    // Test symmetry
-//    actualEquals = filter2.equals(filter1);
-//    assertEquals(actualEquals, expectEquals,
-//                 "Expected " + filter1 + (expectEquals ? " == " : " != ") + filter2);
-//
-//    if (expectEquals) {
-//      assertEquals(filter1.hashCode(), filter2.hashCode(),
-//                   "Hash codes differ for " + filter1 + " and " + filter2);
-//    }
-//
-//    // Test toString
-//    actualEquals = filter2.toString().equals(filter1.toString());
-//    assertEquals(actualEquals, expectStringEquals,
-//                 "Expected " + filter1 + (expectStringEquals ? " == " : " != ") + filter2);
-//  }
+
+  @BeforeClass
+  public void setupClass() throws Exception {
+    TestCaseUtils.startServer();
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////////////
+  //
+  // createFilterFromString
+  //
+  ////////////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////////////
+
+  // -------------------------------------------------------------------------
+  //
+  // Test valid filters.
+  //
+  // -------------------------------------------------------------------------
+
+  // These are valid filters.
+  @DataProvider(name = "paramsCreateFilterFromStringValidFilters")
+  public Object[][] paramsCreateFilterFromStringValidFilters() {
+    return new Object[][]{
+            {"(&)", "(&)"},
+            {"(|)", "(|)"},
+            {"(sn=test)", "(sn=test)"},
+            {"(sn=*)", "(sn=*)"},
+            {"(sn=)", "(sn=)"},
+            {"(sn=*test*)", "(sn=*test*)"},
+
+            {"(!(sn=test))", "(!(sn=test))"},
+            {"(|(sn=test)(sn=test2))", "(|(sn=test)(sn=test2))"},
+
+            {"(&(sn=test))", "(&(sn=test))"},
+            {"(|(sn=test))", "(|(sn=test))"},
+    };
+  }
+
+  @Test(dataProvider = "paramsCreateFilterFromStringValidFilters")
+  public void testCreateFilterFromStringValidFilters(
+          String originalFilter,
+          String expectedToStringFilter
+  ) throws DirectoryException {
+    runRecreateFilterTest(originalFilter, expectedToStringFilter);
+  }
+
+  private void runRecreateFilterTest(
+          String originalFilter,
+          String expectedToStringFilter
+  ) throws DirectoryException {
+    String regenerated = SearchFilter.createFilterFromString(originalFilter).toString();
+    Assert.assertEquals(regenerated, expectedToStringFilter, "original=" + originalFilter + ", expected=" + expectedToStringFilter);
+  }
+
+  // These are valid filters.
+  @DataProvider(name = "escapeSequenceFilters")
+  public Object[][] escapeSequenceFilters() {
+    final char[] CHAR_NIBBLES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+                                 'a', 'b', 'c', 'd', 'e', 'f',
+                                 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    final byte[] BYTE_NIBBLES = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+                                 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+                                 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
+
+    List<String[]> allParameters = new ArrayList<String[]>();
+    for (int i = 0; i < CHAR_NIBBLES.length; i++) {
+      char highNibble = CHAR_NIBBLES[i];
+      byte highByteNibble = BYTE_NIBBLES[i];
+      for (int j = 0; j < CHAR_NIBBLES.length; j++) {
+        char lowNibble = CHAR_NIBBLES[j];
+        byte lowByteNibble = BYTE_NIBBLES[j];
+        String inputChar = "\\" + highNibble + lowNibble;
+        byte byteValue = (byte)((((int)highByteNibble) << 4) | lowByteNibble);
+        String outputChar = getFilterValueForChar(byteValue);
+
+        // Exact match
+        String inputFilter = "(sn=" + inputChar + ")";
+        String outputFilter = "(sn=" + outputChar + ")";
+        allParameters.add(new String[]{inputFilter, outputFilter});
+
+        // Substring
+        inputFilter = "(sn=" + inputChar + "*" + inputChar + "*" + inputChar + ")";
+        outputFilter = "(sn=" + outputChar + "*" + outputChar + "*" + outputChar + ")";
+        allParameters.add(new String[]{inputFilter, outputFilter});
+
+        // <=
+        inputFilter = "(sn<=" + inputChar + ")";
+        outputFilter = "(sn<=" + outputChar + ")";
+        allParameters.add(new String[]{inputFilter, outputFilter});
+
+        // >=
+        inputFilter = "(sn>=" + inputChar + ")";
+        outputFilter = "(sn>=" + outputChar + ")";
+        allParameters.add(new String[]{inputFilter, outputFilter});
+
+        // =~
+        inputFilter = "(sn>=" + inputChar + ")";
+        outputFilter = "(sn>=" + outputChar + ")";
+        allParameters.add(new String[]{inputFilter, outputFilter});
+
+        // =~
+        inputFilter = "(sn:caseExactMatch:=" + inputChar + ")";
+        outputFilter = "(sn:caseExactMatch:=" + outputChar + ")";
+        allParameters.add(new String[]{inputFilter, outputFilter});
+      }
+    }
+
+    return (Object[][]) allParameters.toArray(new String[][]{});
+  }
+
+
+  // These are filters with invalid escape sequences.
+  @DataProvider(name = "invalidEscapeSequenceFilters")
+  public Object[][] invalidEscapeSequenceFilters() {
+    final char[] VALID_NIBBLES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+                                 'a', 'b', 'c', 'd', 'e', 'f',
+                                 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    final char[] INVALID_NIBBBLES = {'g', 'z', 'G', 'Z', '-', '=', '+', '\00', ')',
+                                     'n', 't', '\\'};
+
+    List<String> invalidEscapeSequences = new ArrayList<String>();
+
+    for (int i = 0; i < VALID_NIBBLES.length; i++) {
+      char validNibble = VALID_NIBBLES[i];
+      for (int j = 0; j < INVALID_NIBBBLES.length; j++) {
+        char invalidNibble = INVALID_NIBBBLES[j];
+
+        invalidEscapeSequences.add("\\" + validNibble + invalidNibble);
+        invalidEscapeSequences.add("\\" + invalidNibble + validNibble);
+      }
+      // Also do a test case where we only have one character in the escape sequence.
+      invalidEscapeSequences.add("\\" + validNibble);
+    }
+
+    List<String[]> allParameters = new ArrayList<String[]>();
+    for (String invalidEscape : invalidEscapeSequences) {
+      // Exact match
+      allParameters.add(new String[]{"(sn=" + invalidEscape + ")"});
+      allParameters.add(new String[]{"(sn=" + invalidEscape});
+
+      // Substring
+      allParameters.add(new String[]{"(sn=" + invalidEscape + "*" + invalidEscape + "*" + invalidEscape + ")"});
+      allParameters.add(new String[]{"(sn=" + invalidEscape + "*" + invalidEscape + "*" + invalidEscape});
+
+      // <=
+      allParameters.add(new String[]{"(sn<=" + invalidEscape + ")"});
+      allParameters.add(new String[]{"(sn<=" + invalidEscape});
+
+      // >=
+      allParameters.add(new String[]{"(sn>=" + invalidEscape + ")"});
+      allParameters.add(new String[]{"(sn>=" + invalidEscape});
+
+      // =~
+      allParameters.add(new String[]{"(sn>=" + invalidEscape + ")"});
+      allParameters.add(new String[]{"(sn>=" + invalidEscape});
+
+      // =~
+      allParameters.add(new String[]{"(sn:caseExactMatch:=" + invalidEscape + ")"});
+      allParameters.add(new String[]{"(sn:caseExactMatch:=" + invalidEscape});
+    }
+
+    return (Object[][]) allParameters.toArray(new String[][]{});
+  }
+
+
+  /**
+   * @return a value that can be used in an LDAP filter.
+   */
+  private String getFilterValueForChar(byte value) {
+    if (((value & 0x7F) != value) ||  // Not 7-bit clean
+         (value <= 0x1F) ||           // Below the printable character range
+         (value == 0x28) ||           // Open parenthesis
+         (value == 0x29) ||           // Close parenthesis
+         (value == 0x2A) ||           // Asterisk
+         (value == 0x5C) ||           // Backslash
+         (value == 0x7F))             // Delete character
+    {
+      return "\\" + StaticUtils.byteToHex(value);
+    } else {
+      return "" + ((char)value);
+    }
+  }
+
+  @Test(dataProvider = "escapeSequenceFilters")
+  public void testRecreateFilterWithEscape(
+          String originalFilter,
+          String expectedToStringFilter
+  ) throws DirectoryException {
+    runRecreateFilterTest(originalFilter, expectedToStringFilter);
+  }
+
+  @Test(dataProvider = "invalidEscapeSequenceFilters",
+        expectedExceptions = DirectoryException.class)
+  public void testFilterWithInvalidEscape(
+          String filterWithInvalidEscape)
+          throws DirectoryException {
+    // This should fail with a parse error.
+    SearchFilter.createFilterFromString(filterWithInvalidEscape);
+  }
+
+
+  // -------------------------------------------------------------------------
+  //
+  // Test invalid filters.
+  //
+  // -------------------------------------------------------------------------
+
+  //
+  // Invalid filters that are detected.
+  //
+
+  @DataProvider(name = "invalidFilters")
+  public Object[][] invalidFilters() {
+    return new Object[][]{
+            {null},
+            {"(cn)"},
+            {"()"},
+            {"("},
+            {"(&(sn=test)"},
+            {"(|(sn=test)"},
+            {"(!(sn=test)"},
+            {"(&(sn=test)))"},
+            {"(|(sn=test)))"},
+            // TODO: open a bug for this.
+//            {"(!(sn=test)))"},
+            {"(sn=\\A)"},
+            {"(sn=\\1H)"},
+            {"(sn=\\H1)"},
+    };
+  }
+
+  @Test(dataProvider = "invalidFilters",
+        expectedExceptions = DirectoryException.class)
+  public void testCreateFilterFromStringInvalidFilters(String invalidFilter)
+          throws DirectoryException {
+    SearchFilter.createFilterFromString(invalidFilter).toString();
+  }
+
+  //
+  // This is more or less the same as what's above, but it's for invalid
+  // filters that are not currently detected by the parser.  To turn these
+  // on, remove them from the broken group.  As the code is modified to handle
+  // these cases, please add these test cases to the
+  // paramsCreateFilterFromStringInvalidFilters DataProvider.
+  //
+
+  @DataProvider(name = "uncaughtInvalidFilters")
+  public Object[][] paramsCreateFilterFromStringUncaughtInvalidFilters() {
+    return new Object[][]{
+            {"(cn=**)"},
+            {"( sn = test )"},
+            {"&(cn=*)"},
+            {"(!(sn=test)(sn=test2))"},
+            {"(objectclass=**)"},
+    };
+  }
+
+  @Test(dataProvider = "uncaughtInvalidFilters",
+        expectedExceptions = DirectoryException.class,
+        // FIXME:  These currently aren't detected
+        enabled = false)
+  public void testCreateFilterFromStringUncaughtInvalidFilters(String invalidFilter)
+          throws DirectoryException {
+    SearchFilter.createFilterFromString(invalidFilter).toString();
+  }
+
+
+  ////////////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////////////
+  //
+  // matches
+  //
+  ////////////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////////////
+
+  private static final String JOHN_SMITH_LDIF = TestCaseUtils.makeLdif(
+          "dn: cn=John Smith,dc=example,dc=com",
+          "objectclass: inetorgperson",
+          "cn: John Smith",
+          "cn;lang-en: Jonathan Smith",
+          "sn: Smith",
+          "givenname: John",
+          "internationaliSDNNumber: 12345",
+          "displayName: *",
+          "title: tattoos",
+          "labeledUri: http://opends.org/john"
+          );
+
+  @DataProvider(name = "matchesParams")
+  public Object[][] matchesParams() {
+    return new Object[][]{
+            {JOHN_SMITH_LDIF, "(objectclass=inetorgperson)", true},
+            {JOHN_SMITH_LDIF, "(objectclass=iNetOrgPeRsOn)", true},
+            {JOHN_SMITH_LDIF, "(objectclass=*)", true},
+            {JOHN_SMITH_LDIF, "(objectclass=person)", false},
+
+            {JOHN_SMITH_LDIF, "(cn=John Smith)", true},
+            {JOHN_SMITH_LDIF, "(cn=Jonathan Smith)", true},
+            {JOHN_SMITH_LDIF, "(cn=JOHN SmITh)", true},
+            {JOHN_SMITH_LDIF, "(cn=*)", true},
+            {JOHN_SMITH_LDIF, "(cn=*John Smith*)", true},
+            {JOHN_SMITH_LDIF, "(cn=*Jo*ith*)", true},
+            {JOHN_SMITH_LDIF, "(cn=*Jo*i*th*)", true},
+            {JOHN_SMITH_LDIF, "(cn=*Joh*ohn*)", false},  // this shouldn't match
+            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*23*34*)", false},  // this shouldn't match
+
+            {JOHN_SMITH_LDIF, "(cn=*o*n*)", true},
+            {JOHN_SMITH_LDIF, "(cn=*n*o*)", false},
+
+            // attribute options
+            {JOHN_SMITH_LDIF, "(cn;lang-en=Jonathan Smith)", true},
+            {JOHN_SMITH_LDIF, "(cn;lang-en=Jonathan Smithe)", false},
+            {JOHN_SMITH_LDIF, "(cn;lang-fr=Jonathan Smith)", false},
+            {JOHN_SMITH_LDIF, "(cn;lang-en=*jon*an*)", true},
+
+            // attribute subtypes.  Enable this once 593 is fixed.
+//            {JOHN_SMITH_LDIF, "(name=John Smith)", true},
+//            {JOHN_SMITH_LDIF, "(name=*Smith*)", true},
+//            {JOHN_SMITH_LDIF, "(name;lang-en=Jonathan Smith)", true},  // ? maybe not
+//            {JOHN_SMITH_LDIF, "(name;lang-en=*Jonathan*)", true},  // ? maybe not
+
+            // Enable this once
+//            {JOHN_SMITH_LDIF, "(cn=*Jo**i*th*)", true},
+
+            {JOHN_SMITH_LDIF, "(cn=\\4Aohn*)", true}, // \4A = J
+            {JOHN_SMITH_LDIF, "(|(cn=Jane Smith)(cn=John Smith))", true},
+
+            {JOHN_SMITH_LDIF, "(title~=tattoos)", true},
+            {JOHN_SMITH_LDIF, "(title~=tattos)", true},
+
+            {JOHN_SMITH_LDIF, "(labeledUri=http://opends.org/john)", true},
+            {JOHN_SMITH_LDIF, "(labeledUri=http://opends.org/JOHN)", false},
+            {JOHN_SMITH_LDIF, "(labeledUri=http://*/john)", true},
+            {JOHN_SMITH_LDIF, "(labeledUri=http://*/JOHN)", false},
+
+            {JOHN_SMITH_LDIF, "(cn>=John Smith)", true},
+            {JOHN_SMITH_LDIF, "(cn>=J)", true},
+            {JOHN_SMITH_LDIF, "(cn<=J)", false},
+
+            {JOHN_SMITH_LDIF, "(cn=Jane Smith)", false},
+
+            {JOHN_SMITH_LDIF, "(displayName=\\2A)", true}, // \2A = *
+
+            // 2.5.4.4 is Smith
+            {JOHN_SMITH_LDIF, "(2.5.4.4=Smith)", true},
+
+            {JOHN_SMITH_LDIF, "(sn:caseExactMatch:=Smith)", true},
+            {JOHN_SMITH_LDIF, "(sn:caseExactMatch:=smith)", false},
+
+            // Test cases for 730
+            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*12*45*)", true},
+            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*45*12*)", false},
+
+            // TODO: open a bug for all of these.
+//            {JOHN_SMITH_LDIF, "(:caseExactMatch:=Smith)", true},
+//            {JOHN_SMITH_LDIF, "(:caseExactMatch:=NotSmith)", false},
+
+            // Look at 4515 for some more examples.  Ask Neil.
+//            {JOHN_SMITH_LDIF, "(:dn:caseExactMatch:=example)", true},
+//            {JOHN_SMITH_LDIF, "(:dn:caseExactMatch:=notexample)", false},
+    };
+  }
+
+  @Test(dataProvider = "matchesParams")
+  public void testMatches(String ldifEntry, String filterStr, boolean expectMatch) throws Exception {
+    runMatchTest(ldifEntry, filterStr, expectMatch);
+  }
+
+  private void runMatchTest(String ldifEntry, String filterStr, boolean expectMatch) throws Exception {
+    Entry entry = TestCaseUtils.entryFromLdifString(ldifEntry);
+
+    runSingleMatchTest(entry, filterStr, expectMatch);
+    runSingleMatchTest(entry, "(|" + filterStr + ")", expectMatch);
+    runSingleMatchTest(entry, "(&" + filterStr + ")", expectMatch);
+    runSingleMatchTest(entry, "(!" + filterStr + ")", !expectMatch);
+  }
+
+  private void runSingleMatchTest(Entry entry, String filterStr, boolean expectMatch) throws Exception {
+    final SearchFilter filter = SearchFilter.createFilterFromString(filterStr);
+    boolean matches = filter.matchesEntry(entry);
+    Assert.assertEquals(matches, expectMatch, "Filter=" + filter + "\nEntry=" + entry);
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////////////
+  //
+  // Filter construction
+  //
+  ////////////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////////////
+
+
+  /**
+   *
+   */
+  private static final String makeSimpleLdif(String givenname, String sn) {
+    String cn = givenname + " " + sn;
+    return TestCaseUtils.makeLdif(
+          "dn: cn=" + cn + ",dc=example,dc=com",
+          "objectclass: inetorgperson",
+          "cn: " + cn,
+          "sn: " + sn,
+          "givenname: " + givenname
+          );
+  }
+
+  private static final String JANE_SMITH_LDIF = makeSimpleLdif("Jane", "Smith");
+  private static final String JANE_AUSTIN_LDIF = makeSimpleLdif("Jane", "Austin");
+  private static final String JOE_SMITH_LDIF = makeSimpleLdif("Joe", "Smith");
+  private static final String JOE_AUSTIN_LDIF = makeSimpleLdif("Joe", "Austin");
+
+  private static final List<String> ALL_ENTRIES_LDIF =
+          Collections.unmodifiableList(asList(JANE_SMITH_LDIF,
+                                              JANE_AUSTIN_LDIF,
+                                              JOE_SMITH_LDIF,
+                                              JOE_AUSTIN_LDIF));
+
+
+  /**
+   *
+   */
+  private List<String> getEntriesExcluding(List<String> matchedEntries) {
+    List<String> unmatched = new ArrayList<String>(ALL_ENTRIES_LDIF);
+    unmatched.removeAll(matchedEntries);
+    return unmatched;
+  }
+
+
+  /**
+   *
+   */
+  private static class FilterDescription {
+    private SearchFilter searchFilter;
+
+    private List<String> matchedEntriesLdif;
+    private List<String> unmatchedEntriesLdif;
+
+    private FilterType filterType;
+    private LinkedHashSet<SearchFilter> filterComponents = new LinkedHashSet<SearchFilter>();
+    private SearchFilter notComponent;
+    private AttributeValue assertionValue;
+    private AttributeType attributeType;
+    private ByteString subInitialElement;
+    private List<ByteString> subAnyElements = new ArrayList<ByteString>();
+    private ByteString subFinalElement;
+    private String matchingRuleId;
+    private boolean dnAttributes;
+
+
+    /**
+     *
+     */
+    public void validateFilterFields() throws AssertionError {
+      if (!searchFilter.getFilterType().equals(filterType)) {
+        throwUnequalError("filterTypes");
+      }
+
+      if (!searchFilter.getFilterComponents().equals(filterComponents)) {
+        throwUnequalError("filterComponents");
+      }
+
+      if (!objectsAreEqual(searchFilter.getNotComponent(), notComponent)) {
+        throwUnequalError("notComponent");
+      }
+
+      if (!objectsAreEqual(searchFilter.getAssertionValue(), assertionValue)) {
+        throwUnequalError("assertionValue");
+      }
+
+      if (!objectsAreEqual(searchFilter.getAttributeType(), attributeType)) {
+        throwUnequalError("attributeType");
+      }
+
+      if (!objectsAreEqual(searchFilter.getSubInitialElement(), subInitialElement)) {
+        throwUnequalError("subInitial");
+      }
+
+      if (!objectsAreEqual(searchFilter.getSubAnyElements(), subAnyElements)) {
+        throwUnequalError("subAny");
+      }
+
+      if (!objectsAreEqual(searchFilter.getSubFinalElement(), subFinalElement)) {
+        throwUnequalError("subFinal");
+      }
+
+      if (!objectsAreEqual(searchFilter.getMatchingRuleID(), matchingRuleId)) {
+        throwUnequalError("matchingRuleId");
+      }
+
+      if (searchFilter.getDNAttributes() != dnAttributes) {
+        throwUnequalError("dnAttributes");
+      }
+    }
+
+
+    /**
+     *
+     */
+    private void throwUnequalError(String message) throws AssertionError {
+      throw new AssertionError("Filter differs from what is expected '" + message + "' differ.\n" + toString());
+    }
+
+
+    /**
+     *
+     */
+    @Override
+    public String toString() {
+      return "FilterDescription: \n" +
+              "\tsearchFilter=" + searchFilter + "\n" +
+              "\tfilterType = " + filterType + "\n" +
+              "\tfilterComponents = " + filterComponents + "\n" +
+              "\tnotComponent = " + notComponent + "\n" +
+              "\tassertionValue = " + assertionValue + "\n" +
+              "\tattributeType = " + attributeType + "\n" +
+              "\tsubInitialElement = " + subInitialElement + "\n" +
+              "\tsubAnyElements = " + subAnyElements + "\n" +
+              "\tsubFinalElement = " + subFinalElement + "\n" +
+              "\tmatchingRuleId = " + dnAttributes + "\n";
+    }
+
+
+    /**
+     *
+     */
+    private FilterDescription negate() {
+      FilterDescription negation = new FilterDescription();
+      negation.searchFilter = SearchFilter.createNOTFilter(searchFilter);
+
+      // Flip-flop these
+      negation.matchedEntriesLdif = unmatchedEntriesLdif;
+      negation.unmatchedEntriesLdif = matchedEntriesLdif;
+
+      negation.filterType = FilterType.NOT;
+      negation.notComponent = searchFilter;
+
+      return negation;
+    }
+
+
+    /**
+     *
+     */
+    public FilterDescription clone() {
+      FilterDescription that = new FilterDescription();
+
+      that.searchFilter = this.searchFilter;
+      that.matchedEntriesLdif = this.matchedEntriesLdif;
+      that.unmatchedEntriesLdif = this.unmatchedEntriesLdif;
+      that.filterType = this.filterType;
+      that.filterComponents = this.filterComponents;
+      that.notComponent = this.notComponent;
+      that.assertionValue = this.assertionValue;
+      that.attributeType = this.attributeType;
+      that.subInitialElement = this.subInitialElement;
+      that.subAnyElements = this.subAnyElements;
+      that.subFinalElement = this.subFinalElement;
+      that.matchingRuleId = this.matchingRuleId;
+      that.dnAttributes = this.dnAttributes;
+
+      return that;
+    }
+  }
+
+
+  /**
+   *
+   */
+  private FilterDescription assertionFilterDescription(FilterType filterType,
+                                                       String attributeType,
+                                                       String attributeValue,
+                                                       List<String> matchedEntries) {
+    FilterDescription description = new FilterDescription();
+
+    description.filterType = filterType;
+    description.attributeType = DirectoryServer.getAttributeType(attributeType);
+    description.assertionValue = new AttributeValue(description.attributeType, attributeValue);
+
+    if (filterType == FilterType.EQUALITY) {
+      description.searchFilter = SearchFilter.createEqualityFilter(description.attributeType,
+                                                                   description.assertionValue);
+    } else if (filterType == FilterType.LESS_OR_EQUAL) {
+      description.searchFilter = SearchFilter.createLessOrEqualFilter(description.attributeType,
+                                                                      description.assertionValue);
+    } else if (filterType == FilterType.GREATER_OR_EQUAL) {
+      description.searchFilter = SearchFilter.createGreaterOrEqualFilter(description.attributeType,
+                                                                         description.assertionValue);
+    } else if (filterType == FilterType.APPROXIMATE_MATCH) {
+      description.searchFilter = SearchFilter.createApproximateFilter(description.attributeType,
+                                                                      description.assertionValue);
+    } else {
+      fail(filterType + " is not handled.");
+    }
+
+    description.matchedEntriesLdif = matchedEntries;
+    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
+
+    return description;
+  }
+
+
+  /**
+   *
+   */
+  private FilterDescription equalityFilterDescription(String attributeType,
+                                                      String attributeValue,
+                                                      List<String> matchedEntries) {
+    return assertionFilterDescription(FilterType.EQUALITY, attributeType, attributeValue, matchedEntries);
+  }
+
+
+  /**
+   *
+   */
+  private FilterDescription lessEqualFilterDescription(String attributeType,
+                                                       String attributeValue,
+                                                       List<String> matchedEntries) {
+    return assertionFilterDescription(FilterType.LESS_OR_EQUAL, attributeType, attributeValue, matchedEntries);
+  }
+
+
+  /**
+   *
+   */
+  private FilterDescription greaterEqualFilterDescription(String attributeType,
+                                                          String attributeValue,
+                                                          List<String> matchedEntries) {
+    return assertionFilterDescription(FilterType.GREATER_OR_EQUAL, attributeType, attributeValue, matchedEntries);
+  }
+
+
+  /**
+   *
+   */
+  private FilterDescription approximateFilterDescription(String attributeType,
+                                                         String attributeValue,
+                                                         List<String> matchedEntries) {
+    return assertionFilterDescription(FilterType.APPROXIMATE_MATCH, attributeType, attributeValue, matchedEntries);
+  }
+
+
+  /**
+   *
+   */
+  private FilterDescription substringFilterDescription(String attributeType,
+                                                       String subInitial,
+                                                       List<String> subAny,
+                                                       String subFinal,
+                                                       List<String> matchedEntries) {
+    FilterDescription description = new FilterDescription();
+
+    description.filterType = FilterType.SUBSTRING;
+    description.attributeType = DirectoryServer.getAttributeType(attributeType);
+
+    description.subInitialElement = new ASN1OctetString(subInitial);
+    description.subAnyElements = new ArrayList<ByteString>();
+    for (int i = 0; (subAny != null) && (i < subAny.size()); i++) {
+      String s = subAny.get(i);
+      description.subAnyElements.add(new ASN1OctetString(s));
+    }
+    description.subFinalElement = new ASN1OctetString(subFinal);
+
+    description.searchFilter = SearchFilter.createSubstringFilter(description.attributeType,
+            description.subInitialElement,
+            description.subAnyElements,
+            description.subFinalElement);
+
+
+    description.matchedEntriesLdif = matchedEntries;
+    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
+
+    return description;
+  }
+
+
+  /**
+   *
+   */
+  private List<FilterDescription> getNotFilters(List<FilterDescription> filters) {
+    List<FilterDescription> notFilters = new ArrayList<FilterDescription>();
+
+    for (FilterDescription filter: filters) {
+      notFilters.add(filter.negate());
+    }
+
+    return notFilters;
+  }
+
+
+  /**
+   *
+   */
+  private FilterDescription getAndFilter(List<FilterDescription> filters) {
+    FilterDescription andFilter = new FilterDescription();
+
+    List<String> matchedEntries = new ArrayList<String>(ALL_ENTRIES_LDIF);
+    List<SearchFilter> filterComponents = new ArrayList<SearchFilter>();
+
+    for (FilterDescription filter: filters) {
+      matchedEntries.retainAll(filter.matchedEntriesLdif);
+      filterComponents.add(filter.searchFilter);
+    }
+
+    andFilter.searchFilter = SearchFilter.createANDFilter(filterComponents);
+    andFilter.filterComponents = new LinkedHashSet<SearchFilter>(filterComponents);
+
+    andFilter.filterType = FilterType.AND;
+
+    andFilter.matchedEntriesLdif = matchedEntries;
+    andFilter.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
+
+    return andFilter;
+  }
+
+
+  /**
+   *
+   */
+  private List<FilterDescription> getAndFilters(List<FilterDescription> filters) {
+    List<FilterDescription> andFilters = new ArrayList<FilterDescription>();
+
+    for (FilterDescription first: filters) {
+      for (FilterDescription second: filters) {
+        andFilters.add(getAndFilter(asList(first, second)));
+      }
+    }
+
+    return andFilters;
+  }
+
+
+  /**
+   *
+   */
+  private FilterDescription getOrFilter(List<FilterDescription> filters) {
+    FilterDescription orFilter = new FilterDescription();
+
+    List<String> unmatchedEntries = new ArrayList<String>(ALL_ENTRIES_LDIF);
+    List<SearchFilter> filterComponents = new ArrayList<SearchFilter>();
+
+    for (FilterDescription filter: filters) {
+      unmatchedEntries.retainAll(filter.unmatchedEntriesLdif);
+      filterComponents.add(filter.searchFilter);
+    }
+
+    orFilter.searchFilter = SearchFilter.createORFilter(filterComponents);
+    orFilter.filterComponents = new LinkedHashSet<SearchFilter>(filterComponents);
+
+    orFilter.filterType = FilterType.OR;
+
+    // Since we're not using Sets, we've whittled down unmatched entries from
+    // the full set instead of adding to matchedEntries, which would lead
+    // to duplicates.
+    orFilter.unmatchedEntriesLdif = unmatchedEntries;
+    orFilter.matchedEntriesLdif = getEntriesExcluding(unmatchedEntries);
+
+    return orFilter;
+  }
+
+
+  /**
+   *
+   */
+  private List<FilterDescription> getOrFilters(List<FilterDescription> filters) {
+    List<FilterDescription> orFilters = new ArrayList<FilterDescription>();
+
+    for (FilterDescription first: filters) {
+      for (FilterDescription second: filters) {
+        orFilters.add(getOrFilter(asList(first, second)));
+      }
+    }
+
+    return orFilters;
+  }
+
+
+  /**
+   *
+   */
+  private List<FilterDescription> getEqualityFilters() throws Exception {
+    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
+
+    descriptions.add(equalityFilterDescription("sn", "Smith",
+            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
+
+    descriptions.add(equalityFilterDescription("givenname", "Jane",
+            asList(JANE_SMITH_LDIF, JANE_AUSTIN_LDIF)));
+
+    return descriptions;
+  }
+
+
+  /**
+   *
+   */
+  private List<FilterDescription> getApproximateFilters() throws Exception {
+    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
+
+    descriptions.add(approximateFilterDescription("sn", "Smythe",
+            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
+
+    return descriptions;
+  }
+
+
+  /**
+   *
+   */
+  private List<FilterDescription> getSubstringFilters() throws Exception {
+    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
+
+    descriptions.add(substringFilterDescription(
+            "sn",
+            "S", asList("i"), "th", // S*i*th
+            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
+
+    return descriptions;
+  }
+
+
+  /**
+   *
+   */
+  private List<FilterDescription> getInequalityFilters() throws Exception {
+    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
+
+    descriptions.add(lessEqualFilterDescription("sn", "Aus",
+            (List<String>)(new ArrayList<String>())));
+
+    descriptions.add(greaterEqualFilterDescription("sn", "Aus",
+            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
+                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
+
+
+    descriptions.add(lessEqualFilterDescription("sn", "Smi",
+            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF)));
+
+    descriptions.add(greaterEqualFilterDescription("sn", "Smi",
+            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
+
+
+    descriptions.add(lessEqualFilterDescription("sn", "Smith",
+            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
+                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
+
+    descriptions.add(greaterEqualFilterDescription("sn", "Smith",
+            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
+
+
+    return descriptions;
+  }
+
+
+  /**
+   * Updates to this should also be made in getMinimalFilterDescriptionList.
+   * @see #getMinimalFilterDescriptionList
+   */
+  private List<FilterDescription> getFilterDescriptionList() throws Exception {
+    List<FilterDescription> baseDescriptions = new ArrayList<FilterDescription>();
+
+    baseDescriptions.addAll(getEqualityFilters());
+    baseDescriptions.addAll(getInequalityFilters());
+    baseDescriptions.addAll(getApproximateFilters());
+    baseDescriptions.addAll(getSubstringFilters());
+    baseDescriptions.addAll(getNotFilters(baseDescriptions));
+
+    List<FilterDescription> allDescriptions = new ArrayList<FilterDescription>();
+
+    allDescriptions.addAll(getAndFilters(baseDescriptions));
+    allDescriptions.addAll(getOrFilters(baseDescriptions));
+    allDescriptions.addAll(baseDescriptions);
+
+    return allDescriptions;
+  }
+
+
+  /**
+   *
+   */
+  public List<FilterDescription> getMinimalFilterDescriptionList() throws Exception {
+    List<FilterDescription> baseDescriptions = new ArrayList<FilterDescription>();
+    List<FilterDescription> allDescriptions = new ArrayList<FilterDescription>();
+
+    baseDescriptions.addAll(getEqualityFilters().subList(0, 1));
+    baseDescriptions.addAll(getInequalityFilters().subList(0, 2));
+    baseDescriptions.addAll(getSubstringFilters().subList(0, 1));
+    baseDescriptions.addAll(getNotFilters(baseDescriptions).subList(0, 1));
+
+    allDescriptions.addAll(baseDescriptions);
+    allDescriptions.addAll(getAndFilters(baseDescriptions).subList(0, 2));
+    allDescriptions.addAll(getOrFilters(baseDescriptions).subList(0, 2));
+
+    return allDescriptions;
+  }
+
+
+
+  /**
+   *
+   */
+  @DataProvider(name = "filterDescriptions")
+  public Object[][] getFilterDescriptions() throws Exception {
+    List<FilterDescription> allDescriptions = getFilterDescriptionList();
+
+    // Now convert to [][]
+    FilterDescription[][] descriptionArray = new FilterDescription[allDescriptions.size()][];
+    for (int i = 0; i < allDescriptions.size(); i++) {
+      FilterDescription description = allDescriptions.get(i);
+      descriptionArray[i] = new FilterDescription[]{description};
+    }
+
+    return descriptionArray;
+  }
+
+
+  @Test(dataProvider = "filterDescriptions")
+  public void testFilterConstruction(FilterDescription description) throws Exception {
+    description.validateFilterFields();
+
+    for (String ldif: description.matchedEntriesLdif) {
+      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
+      if (!description.searchFilter.matchesEntry(entry)) {
+        fail("Expected to match entry. " + description + entry);
+      }
+    }
+
+    for (String ldif: description.unmatchedEntriesLdif) {
+      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
+      if (description.searchFilter.matchesEntry(entry)) {
+        fail("Should not have matched entry. " + description + entry);
+      }
+    }
+  }
+
+  // TODO: test more on extensible match and attribute options
+  // TODO: test that we fail when creating filters without specifying all of the parameters
+  // TODO: we need to test attribute options!
+  // TODO: test the audio attribute since it's octetStringMatch
+  // TODO: test the homePhone attribute since   EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstringsMatch
+  // TODO: test labeledURI since it's  caseExactMatch SUBSTR caseExactSubstringsMatch
+  // TODO: test mail since it's EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch
+  // TODO: test secretary since it's distinguishedNameMatch
+  // TODO: test x500UniqueIdentifier since it's bitStringMatch
+
+
+  private static final Object[][] TEST_EQUALS_PARAMS = new Object[][]{
+          // These have duplicates, and their String representation should even reflect that.
+          {"(&(sn=Smith))", "(&(sn=Smith)(sn=Smith))", true, true},
+          {"(|(sn=Smith))", "(|(sn=Smith)(sn=Smith))", true, true},
+
+          // These are reordered, so they are equivalent, but their String representations will differ
+          {"(&(sn=Smith)(sn<=Aus))", "(&(sn<=Aus)(sn=Smith))", true, false},
+          {"(|(sn=Smith)(sn<=Aus))", "(|(sn<=Aus)(sn=Smith))", true, false},
+
+          // These should be case insensitive
+          {"(SN=Smith)", "(sn=Smith)", true, true},
+          {"(sn=smith)", "(sn=Smith)", true, false},
+          {"(SN=S*th)", "(sn=S*th)", true, true},
+
+          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=Smith)", true, true},
+
+          // This demonstrates bug 704.
+//          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=smith)", false, false},
+
+          // TODO: open a bug for this.
+//          {"(:dn:caseExactMatch:=example)", "(:DN:caseExactMatch:=example)", true, true}, // ? String not match
+
+          // 2.5.4.4 is 'sn'
+          {"(2.5.4.4=Smith)", "(2.5.4.4=Smith)", true, true},
+          {"(2.5.4.4=Smith)", "(sn=Smith)", true, true},
+
+          {"(sn;lang-en=Smith)", "(sn;lang-en=Smith)", true, true},
+
+          // This demonstrates bug 706
+//          {"(sn;lang-en=Smith)", "(sn=Smith)", false, false},
+
+
+          // This demonstrates bug 705.
+//          {"(sn=s*t*h)", "(sn=S*T*H)", true, false},
+
+          // These should be case sensitive
+          {"(labeledURI=http://opends.org)", "(labeledURI=http://OpenDS.org)", false, false},
+          {"(labeledURI=http://opends*)", "(labeledURI=http://OpenDS*)", false, false},
+
+          // These are WYSIWIG
+          {"(sn=*)", "(sn=*)", true, true},
+          {"(sn=S*)", "(sn=S*th)", false, false},
+          {"(sn=*S)", "(sn=S*th)", false, false},
+          {"(sn=S*t)", "(sn=S*th)", false, false},
+          {"(sn=*i*t*)", "(sn=*i*t*)", true, true},
+          {"(sn=*t*i*)", "(sn=*i*t*)", false, false},  // Test case for 695
+          {"(sn=S*i*t)", "(sn=S*th)", false, false},
+          {"(sn=Smith)", "(sn=Smith)", true, true},
+          {"(sn=Smith)", "(sn<=Aus)", false, false},
+          {"(sn=Smith)", "(sn>=Aus)", false, false},
+          {"(sn=Smith)", "(sn=S*i*th)", false, false},
+          {"(sn=Smith)", "(!(sn=Smith))", false, false},
+          {"(sn=Smith)", "(&(sn=Smith)(sn<=Aus))", false, false},
+          {"(sn=Smith)", "(|(sn=Smith)(sn<=Aus))", false, false},
+          {"(sn<=Aus)", "(sn<=Aus)", true, true},
+          {"(sn<=Aus)", "(sn>=Aus)", false, false},
+          {"(sn<=Aus)", "(sn=S*i*th)", false, false},
+          {"(sn<=Aus)", "(!(sn=Smith))", false, false},
+          {"(sn<=Aus)", "(&(sn=Smith)(sn=Smith))", false, false},
+          {"(sn<=Aus)", "(&(sn=Smith)(sn<=Aus))", false, false},
+          {"(sn<=Aus)", "(|(sn=Smith)(sn=Smith))", false, false},
+          {"(sn<=Aus)", "(|(sn=Smith)(sn<=Aus))", false, false},
+          {"(sn>=Aus)", "(sn>=Aus)", true, true},
+          {"(sn>=Aus)", "(sn=S*i*th)", false, false},
+          {"(sn>=Aus)", "(!(sn=Smith))", false, false},
+          {"(sn>=Aus)", "(&(sn=Smith)(sn=Smith))", false, false},
+          {"(sn>=Aus)", "(&(sn=Smith)(sn<=Aus))", false, false},
+          {"(sn>=Aus)", "(|(sn=Smith)(sn=Smith))", false, false},
+          {"(sn>=Aus)", "(|(sn=Smith)(sn<=Aus))", false, false},
+          {"(sn=S*i*th)", "(sn=S*i*th)", true, true},
+          {"(sn=S*i*th)", "(!(sn=Smith))", false, false},
+          {"(sn=S*i*th)", "(&(sn=Smith)(sn=Smith))", false, false},
+          {"(sn=S*i*th)", "(&(sn=Smith)(sn<=Aus))", false, false},
+          {"(sn=S*i*th)", "(|(sn=Smith)(sn=Smith))", false, false},
+          {"(sn=S*i*th)", "(|(sn=Smith)(sn<=Aus))", false, false},
+          {"(!(sn=Smith))", "(!(sn=Smith))", true, true},
+          {"(!(sn=Smith))", "(&(sn=Smith)(sn=Smith))", false, false},
+          {"(!(sn=Smith))", "(&(sn=Smith)(sn<=Aus))", false, false},
+          {"(!(sn=Smith))", "(|(sn=Smith)(sn=Smith))", false, false},
+          {"(!(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
+          {"(&(sn=Smith)(sn=Smith))", "(&(sn=Smith)(sn=Smith))", true, true},
+          {"(&(sn=Smith)(sn=Smith))", "(&(sn=Smith)(sn<=Aus))", false, false},
+          {"(&(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn=Smith))", false, false},
+          {"(&(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
+          {"(&(sn=Smith)(sn<=Aus))", "(&(sn=Smith)(sn<=Aus))", true, true},
+          {"(&(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn=Smith))", false, false},
+          {"(&(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn<=Aus))", false, false},
+          {"(|(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn=Smith))", true, true},
+          {"(|(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
+          {"(|(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn<=Aus))", true, true},
+          {"(&(sn=Smith)(sn<=Aus))", "(&(sn=Smith)(sn>=Aus))", false, false},
+          {"(|(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn>=Aus))", false, false},
+
+
+  };
+
+
+  /**
+   *
+   */
+  @DataProvider(name = "equalsTest")
+  public Object[][] getEqualsTests() throws Exception {
+    return TEST_EQUALS_PARAMS;
+  }
+
+
+  /**
+   *
+   */
+  @Test(dataProvider = "equalsTest")
+  public void testEquals(String stringFilter1, String stringFilter2, boolean expectEquals, boolean expectStringEquals) throws Exception {
+    SearchFilter filter1 = SearchFilter.createFilterFromString(stringFilter1);
+    SearchFilter filter2 = SearchFilter.createFilterFromString(stringFilter2);
+
+    boolean actualEquals = filter1.equals(filter2);
+    assertEquals(actualEquals, expectEquals,
+                 "Expected " + filter1 + (expectEquals ? " == " : " != ") + filter2);
+
+    // Test symmetry
+    actualEquals = filter2.equals(filter1);
+    assertEquals(actualEquals, expectEquals,
+                 "Expected " + filter1 + (expectEquals ? " == " : " != ") + filter2);
+
+    if (expectEquals) {
+      assertEquals(filter1.hashCode(), filter2.hashCode(),
+                   "Hash codes differ for " + filter1 + " and " + filter2);
+    }
+
+    // Test toString
+    actualEquals = filter2.toString().equals(filter1.toString());
+    assertEquals(actualEquals, expectStringEquals,
+                 "Expected " + filter1 + (expectStringEquals ? " == " : " != ") + filter2);
+  }
 }
 

--
Gitblit v1.10.0