You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by se...@apache.org on 2020/05/09 18:34:30 UTC

[directory-studio] 01/01: DIRSTUDIO-1250: Fix paged search

This is an automated email from the ASF dual-hosted git repository.

seelmann pushed a commit to branch DIRSTUDIO-1250-fix-paged-search
in repository https://gitbox.apache.org/repos/asf/directory-studio.git

commit f592d7d033b2b1397125355b28f1f0b4430b8b84
Author: Stefan Seelmann <ma...@stefan-seelmann.de>
AuthorDate: Sat May 9 19:23:35 2020 +0200

    DIRSTUDIO-1250: Fix paged search
    
    * Do not use PagedResults.getCookieValue() which is ApacheDS specific
    * Add tests against OpenLDAP
---
 README.md                                          |  11 +-
 .../ldapbrowser/core/jobs/ExportLdifRunnable.java  |   3 +-
 .../core/jobs/InitializeChildrenRunnable.java      |  12 +-
 .../ldapbrowser/core/jobs/SearchRunnable.java      |  12 +-
 tests/test.integration.ui/pom-first.xml            |   2 +
 .../studio/test/integration/ui/OpenLdapTest.java   | 296 +++++++++++++++++++++
 .../studio/test/integration/ui/SearchTest.java     |   1 -
 .../integration/ui/bots/ConnectionsViewBot.java    |  13 +-
 .../studio/test/integration/ui/OpenLdapTest.ldif   | 222 ++++++++++++++++
 9 files changed, 550 insertions(+), 22 deletions(-)

diff --git a/README.md b/README.md
index 4809945..45b70e1 100644
--- a/README.md
+++ b/README.md
@@ -55,14 +55,17 @@ or on Windows :
 
 ### Tests
 
-* Unit tests included in src/test/java of each plugin are executed automatically and run in 'test' phase
-* Core integration tests in tests/test.integration.core are executed automatically and run in 'integration-test' phase
-* SWTBot based UI integration tests in tests/test.integration.ui are disabled by default. They can be enabled with `-Denable-ui-tests`.
-  A failing test generates a screenshot. To not block the developer computer they can run within a virtual framebuffer:
+* Unit tests included in `src/test/java` of each plugin are executed automatically and run in 'test' phase
+* Core integration tests in `tests/test.integration.core` are executed automatically and run in 'integration-test' phase
+* UI integration tests based on SWTBot in `tests/test.integration.ui` are disabled by default. They can be enabled with `-Denable-ui-tests`. A failing test generates a screenshot. To not block the developer computer they can run within a virtual framebuffer:
 
     export DISPLAY=:99
     Xvfb :99 -screen 0 1024x768x16 &
 
+* Some UI integration tests expect a running OpenLDAP server and are skipped otherwise:
+
+    docker run -it --rm -p 20389:389 -p 20636:636 --name openldap osixia/openldap:1.3.0
+
 ### Build issues
 
 Tycho doesn't handle snapshot dependencies well. The first time a snapshot dependency is used within the build it is cached in `~/.m2/repository/p2`. Afterwards any change in the dependency (e.g. ApacheDS or LDAP API) is not considered unless it is deleted from the cache.
diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportLdifRunnable.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportLdifRunnable.java
index 2879339..f559468 100644
--- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportLdifRunnable.java
+++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportLdifRunnable.java
@@ -28,6 +28,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.directory.api.ldap.model.entry.Attribute;
 import org.apache.directory.api.ldap.model.entry.Entry;
 import org.apache.directory.api.ldap.model.entry.Value;
@@ -273,7 +274,7 @@ public class ExportLdifRunnable implements StudioConnectionRunnableWithProgress
                     if ( responseControl instanceof PagedResults )
                     {
                         PagedResults prc = ( PagedResults ) responseControl;
-                        if ( prc.getCookieValue() > 0 )
+                        if ( ArrayUtils.isNotEmpty( prc.getCookie() ) )
                         {
                             // search again: pass the response control cookie to the request control
                             byte[] cookie = prc.getCookie();
diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/InitializeChildrenRunnable.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/InitializeChildrenRunnable.java
index 47e1e14..32eeb12 100644
--- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/InitializeChildrenRunnable.java
+++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/InitializeChildrenRunnable.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.directory.api.ldap.model.message.Control;
 import org.apache.directory.api.ldap.model.message.SearchScope;
 import org.apache.directory.api.ldap.model.message.controls.PagedResults;
@@ -65,7 +66,6 @@ public class InitializeChildrenRunnable implements StudioConnectionBulkRunnableW
     /** The paged search control, only used internally. */
     private PagedResults pagedSearchControl;
 
-
     /**
      * Creates a new instance of InitializeChildrenRunnable.
      * 
@@ -198,7 +198,7 @@ public class InitializeChildrenRunnable implements StudioConnectionBulkRunnableW
     {
         monitor.reportProgress( BrowserCoreMessages.bind( BrowserCoreMessages.jobs__init_entries_progress_sub,
             new String[]
-                { parent.getDn().getName() } ) );
+            { parent.getDn().getName() } ) );
 
         // clear old children
         clearCaches( parent, purgeAllCaches );
@@ -262,7 +262,7 @@ public class InitializeChildrenRunnable implements StudioConnectionBulkRunnableW
                 {
                     if ( search.isPagedSearchScrollMode() )
                     {
-                        if ( prRequestControl.getCookieValue() > 0 )
+                        if ( ArrayUtils.isNotEmpty( prRequestControl.getCookie() ) )
                         {
                             // create top page search runnable, same as original search
                             InitializeChildrenRunnable topPageChildrenRunnable = new InitializeChildrenRunnable(
@@ -270,7 +270,7 @@ public class InitializeChildrenRunnable implements StudioConnectionBulkRunnableW
                             parent.setTopPageChildrenRunnable( topPageChildrenRunnable );
                         }
 
-                        if ( prResponseControl.getCookieValue() > 0 )
+                        if ( ArrayUtils.isNotEmpty( prResponseControl.getCookie() ) )
                         {
                             PagedResults newPrc = Controls.newPagedResultsControl( prRequestControl.getSize(),
                                 prResponseControl.getCookie() );
@@ -282,7 +282,7 @@ public class InitializeChildrenRunnable implements StudioConnectionBulkRunnableW
                     else
                     {
                         // transparently continue search, till count limit is reached
-                        if ( prResponseControl.getCookieValue() > 0
+                        if ( ArrayUtils.isNotEmpty( prResponseControl.getCookie() )
                             && ( search.getCountLimit() == 0 || search.getSearchResults().length < search
                                 .getCountLimit() ) )
                         {
@@ -359,7 +359,7 @@ public class InitializeChildrenRunnable implements StudioConnectionBulkRunnableW
         ISearchResult[] srs = search.getSearchResults();
         monitor.reportProgress( BrowserCoreMessages.bind( BrowserCoreMessages.jobs__init_entries_progress_subcount,
             new String[]
-                { srs == null ? Integer.toString( 0 ) : Integer.toString( srs.length ), parent.getDn().getName() } ) );
+            { srs == null ? Integer.toString( 0 ) : Integer.toString( srs.length ), parent.getDn().getName() } ) );
     }
 
 
diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/SearchRunnable.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/SearchRunnable.java
index e0cf39f..0932ac7 100644
--- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/SearchRunnable.java
+++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/SearchRunnable.java
@@ -31,13 +31,13 @@ import java.util.Set;
 
 import javax.naming.directory.SearchControls;
 
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
 import org.apache.directory.api.ldap.model.entry.Attribute;
 import org.apache.directory.api.ldap.model.exception.LdapException;
 import org.apache.directory.api.ldap.model.message.Control;
 import org.apache.directory.api.ldap.model.message.SearchScope;
 import org.apache.directory.api.ldap.model.message.controls.PagedResults;
-import org.apache.directory.api.ldap.model.message.controls.PagedResultsImpl;
 import org.apache.directory.api.ldap.model.name.Dn;
 import org.apache.directory.api.ldap.model.url.LdapUrl;
 import org.apache.directory.api.util.Strings;
@@ -47,7 +47,6 @@ import org.apache.directory.studio.connection.core.Controls;
 import org.apache.directory.studio.connection.core.Connection.AliasDereferencingMethod;
 import org.apache.directory.studio.connection.core.Connection.ReferralHandlingMethod;
 import org.apache.directory.studio.connection.core.StudioControl;
-import org.apache.directory.studio.connection.core.StudioPagedResultsControl;
 import org.apache.directory.studio.connection.core.io.api.StudioSearchResult;
 import org.apache.directory.studio.connection.core.io.api.StudioSearchResultEnumeration;
 import org.apache.directory.studio.connection.core.jobs.StudioConnectionBulkRunnableWithProgress;
@@ -240,7 +239,7 @@ public class SearchRunnable implements StudioConnectionBulkRunnableWithProgress
                         nextPageSearch.getControls().add( nextPrc );
                         if ( search.isPagedSearchScrollMode() )
                         {
-                            if ( prRequestControl.getCookieValue() > 0 )
+                            if ( ArrayUtils.isNotEmpty( prRequestControl.getCookie() ) )
                             {
                                 // create top page search runnable, same as original search
                                 ISearch topPageSearch = ( ISearch ) search.clone();
@@ -248,7 +247,7 @@ public class SearchRunnable implements StudioConnectionBulkRunnableWithProgress
                                 SearchRunnable topPageSearchRunnable = new SearchRunnable( search, topPageSearch );
                                 search.setTopPageSearchRunnable( topPageSearchRunnable );
                             }
-                            if ( prResponseControl.getCookieValue() > 0 )
+                            if ( ArrayUtils.isNotEmpty( prResponseControl.getCookie() ) )
                             {
                                 // create next page search runnable
                                 SearchRunnable nextPageSearchRunnable = new SearchRunnable( search, nextPageSearch );
@@ -258,7 +257,7 @@ public class SearchRunnable implements StudioConnectionBulkRunnableWithProgress
                         else
                         {
                             // transparently continue search, till count limit is reached
-                            if ( prResponseControl.getCookieValue() > 0
+                            if ( ArrayUtils.isNotEmpty( prResponseControl.getCookie() )
                                 && ( search.getCountLimit() == 0 || search.getSearchResults().length < search
                                     .getCountLimit() ) )
                             {
@@ -406,7 +405,8 @@ public class SearchRunnable implements StudioConnectionBulkRunnableWithProgress
                             search.getResponseControls().add( control );
                             if ( control instanceof PagedResults )
                             {
-                                search.setCountLimitExceeded( ( ( PagedResults ) control ).getCookieValue() > 0 );
+                                search.setCountLimitExceeded(
+                                    ArrayUtils.isNotEmpty( ( ( PagedResults ) control ).getCookie() ) );
                             }
                         }
                     }
diff --git a/tests/test.integration.ui/pom-first.xml b/tests/test.integration.ui/pom-first.xml
index b564e0d..9079550 100644
--- a/tests/test.integration.ui/pom-first.xml
+++ b/tests/test.integration.ui/pom-first.xml
@@ -69,6 +69,8 @@
  org.apache.directory.api.ldap.codec.core;bundle-version="${org.apache.directory.api.bundleversion}",
  org.apache.directory.api.ldap.model;bundle-version="${org.apache.directory.api.bundleversion}",
  org.apache.directory.api.ldap.net.mina;bundle-version="${org.apache.directory.api.bundleversion}",
+ org.apache.directory.api.ldap.schema;bundle-version="${org.apache.directory.api.bundleversion}",
+ org.apache.directory.api.ldap.schema.converter;bundle-version="${org.apache.directory.api.bundleversion}",
  org.apache.directory.api.util;bundle-version="${org.apache.directory.api.bundleversion}",
  org.apache.mina.core;bundle-version="${org.apache.mina.bundleversion}",
  
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/OpenLdapTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/OpenLdapTest.java
new file mode 100644
index 0000000..72d94f4
--- /dev/null
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/OpenLdapTest.java
@@ -0,0 +1,296 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+
+package org.apache.directory.studio.test.integration.ui;
+
+
+import static org.apache.directory.studio.test.integration.ui.Constants.LOCALHOST;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
+import org.apache.directory.api.ldap.model.ldif.LdifEntry;
+import org.apache.directory.api.ldap.model.ldif.LdifReader;
+import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.directory.ldap.client.api.exception.InvalidConnectionException;
+import org.apache.directory.studio.connection.core.Connection;
+import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
+import org.apache.directory.studio.test.integration.ui.bots.BrowserViewBot;
+import org.apache.directory.studio.test.integration.ui.bots.ConnectionsViewBot;
+import org.apache.directory.studio.test.integration.ui.bots.NewConnectionWizardBot;
+import org.apache.directory.studio.test.integration.ui.bots.SearchDialogBot;
+import org.apache.directory.studio.test.integration.ui.bots.StudioBot;
+import org.apache.directory.studio.test.integration.ui.bots.utils.Assertions;
+import org.apache.directory.studio.test.integration.ui.bots.utils.FrameworkRunnerWithScreenshotCaptureListener;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Tests against OpenLDAP.
+ * 
+ * Expects a running OpenLDAP server with below default connection parameters
+ * which can be configured via environment variables.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+@RunWith(FrameworkRunnerWithScreenshotCaptureListener.class)
+public class OpenLdapTest
+{
+    static String getOrDefault( String key, String defaultValue )
+    {
+        return System.getenv().getOrDefault( key, defaultValue );
+    }
+
+    private static final String OPENLDAP_HOST = getOrDefault( "OPENLDAP_HOST", LOCALHOST );
+    private static final int OPENLDAP_PORT = Integer.parseInt( getOrDefault( "OPENLDAP_PORT", "20389" ) );
+    private static final String OPENLDAP_ADMIN_DN = getOrDefault( "OPENLDAP_ADMIN_DN", "cn=admin,dc=example,dc=org" );
+    private static final String OPENLDAP_ADMIN_PASSWORD = getOrDefault( "OPENLDAP_ADMIN_PASSWORD", "admin" );
+
+    @BeforeClass
+    public static void skipOpenLdapTestIfNotRunning() throws Exception
+    {
+
+        try ( LdapNetworkConnection connection = new LdapNetworkConnection( OPENLDAP_HOST, OPENLDAP_PORT ) )
+        {
+            connection.connect();
+            connection.bind( OPENLDAP_ADMIN_DN, OPENLDAP_ADMIN_PASSWORD );
+        }
+        catch ( InvalidConnectionException e )
+        {
+            Assume.assumeNoException(
+                "Skipping tests as connection to OpenLDAP server failed: " + OPENLDAP_HOST + ":" + OPENLDAP_PORT, e );
+        }
+        catch ( LdapAuthenticationException e )
+        {
+            Assume.assumeNoException(
+                "Skipping tests as bind to OpenLDAP server failed: " + OPENLDAP_HOST + ":" + OPENLDAP_PORT, e );
+        }
+    }
+
+    private StudioBot studioBot;
+    private ConnectionsViewBot connectionsViewBot;
+    private BrowserViewBot browserViewBot;
+
+    private Connection connection;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        studioBot = new StudioBot();
+        studioBot.resetLdapPerspective();
+        connectionsViewBot = studioBot.getConnectionView();
+        connection = connectionsViewBot.createTestConnection( "OpenLdapTest", OPENLDAP_HOST, OPENLDAP_PORT,
+            OPENLDAP_ADMIN_DN, OPENLDAP_ADMIN_PASSWORD );
+        browserViewBot = studioBot.getBrowserView();
+
+        try ( LdapNetworkConnection connection = new LdapNetworkConnection( OPENLDAP_HOST, OPENLDAP_PORT );
+            LdifReader ldifReader = new LdifReader( OpenLdapTest.class.getResourceAsStream( "OpenLdapTest.ldif" ) ) )
+        {
+            connection.bind( OPENLDAP_ADMIN_DN, OPENLDAP_ADMIN_PASSWORD );
+            for ( LdifEntry entry : ldifReader )
+            {
+                connection.add( entry.getEntry() );
+            }
+        }
+    }
+
+
+    @After
+    public void tearDown() throws Exception
+    {
+        connectionsViewBot.deleteTestConnections();
+        Assertions.genericTearDownAssertions();
+
+        try ( LdapNetworkConnection connection = new LdapNetworkConnection( OPENLDAP_HOST, OPENLDAP_PORT );
+            LdifReader ldifReader = new LdifReader( OpenLdapTest.class.getResourceAsStream( "OpenLdapTest.ldif" ) ) )
+        {
+            connection.bind( OPENLDAP_ADMIN_DN, OPENLDAP_ADMIN_PASSWORD );
+            List<LdifEntry> ldifEntries = StreamSupport.stream( ldifReader.spliterator(), false )
+                .collect( Collectors.toList() );
+            Collections.reverse( ldifEntries );
+            for ( LdifEntry entry : ldifEntries )
+            {
+                connection.delete( entry.getDn() );
+            }
+        }
+    }
+
+
+    @Test
+    public void testBrowseWithPagingWithScrollMode()
+    {
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+
+        // enable Simple Paged Results control
+        connection.getConnectionParameter().setExtendedBoolProperty(
+            IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH, true );
+        connection.getConnectionParameter().setExtendedIntProperty(
+            IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH_SIZE, 3 );
+        connection.getConnectionParameter().setExtendedBoolProperty(
+            IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH_SCROLL_MODE, true );
+
+        // 1st page
+        browserViewBot.expandEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+        assertFalse(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+        assertTrue(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+
+        // next page
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" );
+        assertTrue(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+        assertTrue(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+
+        // last page
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" );
+        assertTrue(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+        assertFalse(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+
+        // back to top
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" );
+        assertFalse(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+        assertTrue(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+    }
+
+
+    @Test
+    public void testBrowseWithPagingWithoutScrollMode()
+    {
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+
+        // enable Simple Paged Results control
+        connection.getConnectionParameter().setExtendedBoolProperty(
+            IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH, true );
+        connection.getConnectionParameter().setExtendedIntProperty(
+            IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH_SIZE, 3 );
+        connection.getConnectionParameter().setExtendedBoolProperty(
+            IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH_SCROLL_MODE, false );
+
+        browserViewBot.expandEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+        assertFalse(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+        assertFalse(
+            browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+        assertTrue( browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users (8)" ) );
+        assertTrue( browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.1" ) );
+        assertTrue( browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.8" ) );
+    }
+
+
+    @Test
+    public void testSearchWithPagingWithScrollMode() throws Exception
+    {
+        String searchName = "Paged search with scroll mode";
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+        SearchDialogBot dialogBot = browserViewBot.openSearchDialog();
+        assertTrue( dialogBot.isVisible() );
+        dialogBot.setSearchName( searchName );
+        dialogBot.setFilter( "(objectClass=*)" );
+        dialogBot.setReturningAttributes( "objectClass,ou,cn,uid" );
+        dialogBot.setControlPagedSearch( true, 3, true );
+        dialogBot.clickSearchButton();
+
+        // 1st page
+        browserViewBot.expandEntry( "Searches", searchName );
+        assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+        assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+
+        // next page
+        browserViewBot.selectEntry( "Searches", searchName, "--- Next Page ---" );
+        assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+        assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+
+        // last page
+        browserViewBot.selectEntry( "Searches", searchName, "--- Next Page ---" );
+        assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+        assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+
+        // back to top
+        browserViewBot.selectEntry( "Searches", searchName, "--- Top Page ---" );
+        assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+        assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+    }
+
+
+    @Test
+    public void testSearchWithPagingWithoutScrollMode() throws Exception
+    {
+        String searchName = "Paged search without scroll mode";
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+        SearchDialogBot dialogBot = browserViewBot.openSearchDialog();
+        assertTrue( dialogBot.isVisible() );
+        dialogBot.setSearchName( searchName );
+        dialogBot.setFilter( "(objectClass=*)" );
+        dialogBot.setReturningAttributes( "objectClass,ou,cn,uid" );
+        dialogBot.setControlPagedSearch( true, 3, false );
+        dialogBot.clickSearchButton();
+
+        browserViewBot.expandEntry( "Searches", searchName );
+        assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+        assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+        assertTrue( browserViewBot.existsEntry( "Searches", searchName + " (9+)" ) );
+        assertTrue( browserViewBot.existsEntry( "Searches", searchName, "uid=user.1" ) );
+        assertTrue( browserViewBot.existsEntry( "Searches", searchName, "uid=user.8" ) );
+    }
+
+
+    @Test
+    public void testCheckAuthenticationButtonNotOK()
+    {
+        // enter connection parameter
+        NewConnectionWizardBot wizardBot = connectionsViewBot.openNewConnectionWizard();
+        wizardBot.typeConnectionName( "OpenLDAP connection test not ok" );
+        wizardBot.typeHost( OPENLDAP_HOST );
+        wizardBot.typePort( OPENLDAP_PORT );
+        wizardBot.clickNextButton();
+
+        // enter incorrect authentication parameter
+        wizardBot.typeUser( OPENLDAP_ADMIN_DN );
+        wizardBot.typePassword( "wrongpassword" );
+
+        // click "Check Network Parameter" button
+        String result = wizardBot.clickCheckAuthenticationButton();
+        assertNotNull( "Expected Error", result );
+        assertThat( result, containsString( "[LDAP result code 49 - invalidCredentials]" ) );
+
+        wizardBot.clickCancelButton();
+    }
+
+}
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/SearchTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/SearchTest.java
index 71d6c61..eb07d1c 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/SearchTest.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/SearchTest.java
@@ -367,7 +367,6 @@ public class SearchTest extends AbstractLdapTestUnit
         assertTrue( dialogBot.isVisible() );
         dialogBot.setSearchName( searchName );
         dialogBot.setReturningAttributes( "objectClass,ou,cn,uid" );
-        dialogBot.setControlPagedSearch( true, 5, false );
 
         FilterEditorDialogBot filterBot = dialogBot.openFilterEditor();
         filterBot.setFilter( "(&(objectClass=*)(uid=user.1))" );
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/ConnectionsViewBot.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/ConnectionsViewBot.java
index cc39443..bd7ca88 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/ConnectionsViewBot.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/ConnectionsViewBot.java
@@ -48,7 +48,6 @@ public class ConnectionsViewBot
 {
     private SWTWorkbenchBot bot = new SWTWorkbenchBot();
 
-
     public NewConnectionWizardBot openNewConnectionWizard()
     {
         ContextMenuHelper.clickContextMenu( getConnectionsTree(), "New Connection..." );
@@ -163,17 +162,23 @@ public class ConnectionsViewBot
      */
     public Connection createTestConnection( String name, int port ) throws Exception
     {
+        return createTestConnection( name, LOCALHOST, port, "uid=admin,ou=system", "secret" );
+    }
+
+
+    public Connection createTestConnection( String name, String host, int port, String bindDn, String bindPassword ) throws Exception
+    {
         name = name + "_" + System.currentTimeMillis();
 
         ConnectionManager connectionManager = ConnectionCorePlugin.getDefault().getConnectionManager();
         ConnectionParameter connectionParameter = new ConnectionParameter();
         connectionParameter.setName( name );
-        connectionParameter.setHost( LOCALHOST );
+        connectionParameter.setHost( host );
         connectionParameter.setPort( port );
         connectionParameter.setEncryptionMethod( EncryptionMethod.NONE );
         connectionParameter.setAuthMethod( AuthenticationMethod.SIMPLE );
-        connectionParameter.setBindPrincipal( "uid=admin,ou=system" );
-        connectionParameter.setBindPassword( "secret" );
+        connectionParameter.setBindPrincipal( bindDn );
+        connectionParameter.setBindPassword( bindPassword );
         Connection connection = new Connection( connectionParameter );
         connectionManager.addConnection( connection );
 
diff --git a/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/OpenLdapTest.ldif b/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/OpenLdapTest.ldif
new file mode 100644
index 0000000..d64ed89
--- /dev/null
+++ b/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/OpenLdapTest.ldif
@@ -0,0 +1,222 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+#
+dn: ou=users,dc=example,dc=org
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+
+dn: uid=user.1,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aaccf
+sn: Amar
+cn: Aaccf Amar
+initials: AA
+uid: user.1
+mail: user.1@null
+userPassword: password
+telephoneNumber: 976-893-3312
+homePhone: 337-310-0727
+pager: 185-156-4071
+mobile: 626-188-0934
+employeeNumber: 1
+street: 27919 Broadway Street
+l: Tallahassee
+st: DE
+postalCode: 67698
+postalAddress: Aaccf Amar$27919 Broadway Street$Tallahassee, DE  67698
+description: This is the description for Aaccf Amar.
+roomNumber: 1388
+
+dn: uid=user.2,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aaren
+sn: Atp
+cn: Aaren Atp
+initials: AA
+uid: user.2
+mail: user.2@null
+userPassword: password
+telephoneNumber: 147-953-0396
+homePhone: 174-439-5329
+pager: 617-443-8449
+mobile: 045-464-2512
+employeeNumber: 2
+street: 36109 Center Street
+l: Harlingen
+st: CO
+postalCode: 21733
+postalAddress: Aaren Atp$36109 Center Street$Harlingen, CO  21733
+description: This is the description for Aaren Atp.
+roomNumber: 1198
+
+dn: uid=user.3,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aarika
+sn: Atpco
+cn: Aarika Atpco
+initials: AA
+uid: user.3
+mail: user.3@null
+userPassword: password
+telephoneNumber: 925-356-4943
+homePhone: 099-983-0308
+pager: 806-672-7363
+mobile: 425-493-8009
+employeeNumber: 3
+street: 14730 Fourteenth Street
+l: Evansville
+st: ND
+postalCode: 95526
+postalAddress: Aarika Atpco$14730 Fourteenth Street$Evansville, ND  95526
+description: This is the description for Aarika Atpco.
+roomNumber: 1135
+
+dn: uid=user.4,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aaron
+sn: Atrc
+cn: Aaron Atrc
+initials: AA
+uid: user.4
+mail: user.4@null
+userPassword: password
+telephoneNumber: 696-981-0488
+homePhone: 854-401-1330
+pager: 389-028-9518
+mobile: 117-882-4912
+employeeNumber: 4
+street: 98495 Fifteenth Street
+l: Miami
+st: PA
+postalCode: 16887
+postalAddress: Aaron Atrc$98495 Fifteenth Street$Miami, PA  16887
+description: This is the description for Aaron Atrc.
+roomNumber: 1311
+
+dn: uid=user.5,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aartjan
+sn: Aalders
+cn: Aartjan Aalders
+initials: AA
+uid: user.5
+mail: user.5@null
+userPassword: password
+telephoneNumber: 657-627-9753
+homePhone: 109-158-4533
+pager: 801-392-0010
+mobile: 763-973-0947
+employeeNumber: 5
+street: 52408 Ridge Street
+l: Omaha
+st: AR
+postalCode: 08015
+postalAddress: Aartjan Aalders$52408 Ridge Street$Omaha, AR  08015
+description: This is the description for Aartjan Aalders.
+roomNumber: 1090
+
+dn: uid=user.6,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Abagael
+sn: Aasen
+cn: Abagael Aasen
+initials: AA
+uid: user.6
+mail: user.6@null
+userPassword: password
+telephoneNumber: 600-556-9017
+homePhone: 543-731-7797
+pager: 349-743-6572
+mobile: 042-921-9717
+employeeNumber: 6
+street: 87514 Dogwood Street
+l: Bloomington
+st: ID
+postalCode: 56762
+postalAddress: Abagael Aasen$87514 Dogwood Street$Bloomington, ID  56762
+description: This is the description for Abagael Aasen.
+roomNumber: 1449
+
+dn: uid=user.7,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Abagail
+sn: Abadines
+cn: Abagail Abadines
+initials: AA
+uid: user.7
+mail: user.7@null
+userPassword: password
+telephoneNumber: 628-294-0713
+homePhone: 313-764-3970
+pager: 118-541-2978
+mobile: 111-360-9243
+employeeNumber: 7
+street: 60751 Main Street
+l: Macon
+st: MI
+postalCode: 38755
+postalAddress: Abagail Abadines$60751 Main Street$Macon, MI  38755
+description: This is the description for Abagail Abadines.
+roomNumber: 1312
+
+dn: uid=user.8,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Abahri
+sn: Abazari
+cn: Abahri Abazari
+initials: AA
+uid: user.8
+mail: user.8@null
+userPassword: password
+telephoneNumber: 760-373-5805
+homePhone: 856-861-3648
+pager: 271-218-0027
+mobile: 957-465-6928
+employeeNumber: 8
+street: 81023 River Street
+l: Monroe
+st: SD
+postalCode: 51865
+postalAddress: Abahri Abazari$81023 River Street$Monroe, SD  51865
+description: This is the description for Abahri Abazari.
+roomNumber: 1963
+