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 2021/01/25 05:18:26 UTC

[directory-studio] branch DIRSTUDIO-744-fix-entry-modify-request updated (294c29d -> cfc7dc1)

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

seelmann pushed a change to branch DIRSTUDIO-744-fix-entry-modify-request
in repository https://gitbox.apache.org/repos/asf/directory-studio.git.


 discard 294c29d  DIRSTUDIO-744: Fix entry modify requests
     new cfc7dc1  DIRSTUDIO-744: Fix entry modify requests

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (294c29d)
            \
             N -- N -- N   refs/heads/DIRSTUDIO-744-fix-entry-modify-request (cfc7dc1)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../directory/studio/test/integration/ui/bots/AbstractLogsViewBot.java | 3 ++-
 .../studio/test/integration/ui/bots/EntryEditorWidgetBot.java          | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)


[directory-studio] 01/01: DIRSTUDIO-744: Fix entry modify requests

Posted by se...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

seelmann pushed a commit to branch DIRSTUDIO-744-fix-entry-modify-request
in repository https://gitbox.apache.org/repos/asf/directory-studio.git

commit cfc7dc194456a85bf8a3088e96cc47daa499bc8f
Author: Stefan Seelmann <ma...@stefan-seelmann.de>
AuthorDate: Sun Jan 24 21:26:35 2021 +0100

    DIRSTUDIO-744: Fix entry modify requests
    
    * When deleting all remaining values of an attribute the deleted values are now included in the modify request.
    * Remove "smart" behaviour whicht used replace operation instead of delete+add when modifying a value.
    * Both can cause unwanted side effect if the client doesn't see all values.
---
 .../docbook/2.40_tools_newconnection_wizard.xml    |  22 +-
 .../docbook/2.80_tools_connection_properties.xml   |  18 +-
 .../common/widgets/connection/messages.properties  |   6 +-
 .../studio/ldapbrowser/core/utils/Utils.java       |  15 +-
 tests/test.integration.core/pom-first.xml          |   1 +
 .../test/integration/core/ComputeDiffTest.java     | 309 +++++++++++++++++++++
 .../test/integration/ui/EntryEditorTest.java       |  70 ++++-
 .../test/integration/ui/ErrorHandlingTest.java     |   3 +-
 .../studio/test/integration/ui/OpenLdapTest.java   | 133 +++++++++
 .../integration/ui/bots/AbstractLogsViewBot.java   |   3 +-
 .../integration/ui/bots/EntryEditorWidgetBot.java  |   3 +-
 .../studio/test/integration/ui/OpenLdapTest.ldif   |   1 -
 12 files changed, 540 insertions(+), 44 deletions(-)

diff --git a/helps/ldapbrowser.help/src/main/docbook/2.40_tools_newconnection_wizard.xml b/helps/ldapbrowser.help/src/main/docbook/2.40_tools_newconnection_wizard.xml
index 19f1a6d..c5312b6 100644
--- a/helps/ldapbrowser.help/src/main/docbook/2.40_tools_newconnection_wizard.xml
+++ b/helps/ldapbrowser.help/src/main/docbook/2.40_tools_newconnection_wizard.xml
@@ -574,15 +574,15 @@
 								Specify the modify mode for attributes with an equality matching rule.
 								Description of options:
 								<itemizedlist spacing="normal" mark="bullet">
-									<listitem>Optimized Modify Operations: uses add/delete by default, 
-									uses replace if operation count is less</listitem>
+									<listitem>Default: uses add/delete by default, 
+									uses replace for X-ORDERED attributes</listitem>
 									<listitem>Always REPLACE: always uses replace operations to perform 
 									entry modifications</listitem>
 									<listitem>Always ADD/DELETE: always uses add and/or delete operations 
 									to perform entry modifications</listitem>
 								</itemizedlist>
 							</entry>
-							<entry>Optimized Modify Operations</entry>
+							<entry>Default</entry>
 						</row>
 						<row>
 							<entry>Modify Mode (no equality matching rule)</entry>
@@ -590,8 +590,8 @@
 								Specify the modify mode for attributes with *no* equality matching rule.
 								Description of options:
 								<itemizedlist spacing="normal" mark="bullet">
-									<listitem>Optimized Modify Operations: uses add/delete by default, 
-									uses replace if operation count is less</listitem>
+									<listitem>Default: uses add/delete by default, 
+									uses replace for X-ORDERED attributes</listitem>
 									<listitem>Always REPLACE: always uses replace operations to perform 
 									entry modifications</listitem>
 									<listitem>Always ADD/DELETE: always uses add and/or delete operations 
@@ -599,18 +599,18 @@
 								</itemizedlist>
 								Recommended values for various LDAP servers:
 								<itemizedlist spacing="normal" mark="bullet">
-									<listitem>ApacheDS: Optimized Modify Operations or REPLACE</listitem>
+									<listitem>ApacheDS: Default or REPLACE</listitem>
 									<listitem>OpenLDAP: REPLACE</listitem>
-									<listitem>OpenDS / SunDSEE: Optimized Modify Operations or REPLACE</listitem>
-									<listitem>FedoraDS / 389DS: Optimized Modify Operations 
+									<listitem>OpenDS / SunDSEE: Default or REPLACE</listitem>
+									<listitem>FedoraDS / 389DS: Default 
 									(missing equality matching rules for many standard attribute types)</listitem>
-									<listitem>Active Directory: Optimized Modify Operations 
+									<listitem>Active Directory: Default 
 									(exposes no equality matching rules at all)</listitem>
-									<listitem>eDirectory: Optimized Modify Operations 
+									<listitem>eDirectory: Default 
 									(exposes no equality matching rules at all)</listitem>
 								</itemizedlist>
 							</entry>
-							<entry>Optimized Modify Operations</entry>
+							<entry>Default</entry>
 						</row>
 						<row>
 							<entry>Modify Order</entry>
diff --git a/helps/ldapbrowser.help/src/main/docbook/2.80_tools_connection_properties.xml b/helps/ldapbrowser.help/src/main/docbook/2.80_tools_connection_properties.xml
index c080548..68ed5f4 100644
--- a/helps/ldapbrowser.help/src/main/docbook/2.80_tools_connection_properties.xml
+++ b/helps/ldapbrowser.help/src/main/docbook/2.80_tools_connection_properties.xml
@@ -437,8 +437,8 @@
 							Specify the modify mode for attributes with an equality matching rule.
 							Description of options:
 							<itemizedlist spacing="normal" mark="bullet">
-								<listitem>Optimized Modify Operations: uses add/delete by default, 
-								uses replace if operation count is less</listitem>
+								<listitem>Default: uses add/delete by default, 
+								uses replace for X-ORDERED attributes</listitem>
 								<listitem>Always REPLACE: always uses replace operations to perform 
 								entry modifications</listitem>
 								<listitem>Always ADD/DELETE: always uses add and/or delete operations 
@@ -452,8 +452,8 @@
 							Specify the modify mode for attributes with *no* equality matching rule.
 							Description of options:
 							<itemizedlist spacing="normal" mark="bullet">
-								<listitem>Optimized Modify Operations: uses add/delete by default, 
-								uses replace if operation count is less</listitem>
+								<listitem>Default: uses add/delete by default, 
+								uses replace for X-ORDERED attributes</listitem>
 								<listitem>Always REPLACE: always uses replace operations to perform 
 								entry modifications</listitem>
 								<listitem>Always ADD/DELETE: always uses add and/or delete operations 
@@ -461,14 +461,14 @@
 							</itemizedlist>
 							Recommended values for various LDAP servers:
 							<itemizedlist spacing="normal" mark="bullet">
-								<listitem>ApacheDS: Optimized Modify Operations or REPLACE</listitem>
+								<listitem>ApacheDS: Default or REPLACE</listitem>
 								<listitem>OpenLDAP: REPLACE</listitem>
-								<listitem>OpenDS / SunDSEE: Optimized Modify Operations or REPLACE</listitem>
-								<listitem>FedoraDS / 389DS: Optimized Modify Operations 
+								<listitem>OpenDS / SunDSEE: Default or REPLACE</listitem>
+								<listitem>FedoraDS / 389DS: Default 
 								(missing equality matching rules for many standard attribute types)</listitem>
-								<listitem>Active Directory: Optimized Modify Operations 
+								<listitem>Active Directory: Default 
 								(exposes no equality matching rules at all)</listitem>
-								<listitem>eDirectory: Optimized Modify Operations 
+								<listitem>eDirectory: Default 
 								(exposes no equality matching rules at all)</listitem>
 							</itemizedlist>
 						</entry>
diff --git a/plugins/ldapbrowser.common/src/main/java/org/apache/directory/studio/ldapbrowser/common/widgets/connection/messages.properties b/plugins/ldapbrowser.common/src/main/java/org/apache/directory/studio/ldapbrowser/common/widgets/connection/messages.properties
index fd9548f..b1d9963 100644
--- a/plugins/ldapbrowser.common/src/main/java/org/apache/directory/studio/ldapbrowser/common/widgets/connection/messages.properties
+++ b/plugins/ldapbrowser.common/src/main/java/org/apache/directory/studio/ldapbrowser/common/widgets/connection/messages.properties
@@ -38,11 +38,11 @@ BrowserParameterPage.ManageDsaItWhileBrowsingTooltip=If enabled the ManageDsaIT
 EditorParameterPage.ModifyGroup=Entry Modifcation
 EditorParameterPage.ModifyMode=Modify Mode:
 EditorParameterPage.ModifyModeAddDel=Always use ADD and/or DELETE
-EditorParameterPage.ModifyModeDefault=Optimized Modify Operations
+EditorParameterPage.ModifyModeDefault=Default
 EditorParameterPage.ModifyModeNoEMR=Modify Mode (no equality matching rule):
-EditorParameterPage.ModifyModeNoEMRTooltip=Specify the modify mode for attributes with *no* equality matching rule.\n\nDescription of options:\n* Optimized Modify Operations: uses add/delete by default, uses replace if operation count is less\n* Always use REPLACE: always uses replace operations to perform entry modifications\n* Always use ADD and/or DELETE: always uses add and/or delete operations to perform entry modifications\n\nRecommended values for various LDAP servers:\n* ApacheDS [...]
+EditorParameterPage.ModifyModeNoEMRTooltip=Specify the modify mode for attributes with *no* equality matching rule.\n\nDescription of options:\n* Default: uses add/delete by default, uses replace for X-ORDERED attributes\n* Always use REPLACE: always uses replace operations to perform entry modifications\n* Always use ADD and/or DELETE: always uses add and/or delete operations to perform entry modifications\n\nRecommended values for various LDAP servers:\n* ApacheDS: Default or REPLACE\n [...]
 EditorParameterPage.ModifyModeReplace=Always use REPLACE
-EditorParameterPage.ModifyModeTooltip=Specify the modify mode for attributes with an equality matching rule.\n\nDescription of options:\n* Optimized Modify Operations: uses add/delete by default, uses replace if operation count is less\n* Always REPLACE: always uses replace operations to perform entry modifications\n* Always ADD/DELETE: always uses add and/or delete operations to perform entry modifications\n\nRecommended value: Optimized Modify Operations
+EditorParameterPage.ModifyModeTooltip=Specify the modify mode for attributes with an equality matching rule.\n\nDescription of options:\n* Default: uses add/delete by default, uses replace for X-ORDERED attributes\n* Always REPLACE: always uses replace operations to perform entry modifications\n* Always ADD/DELETE: always uses add and/or delete operations to perform entry modifications\n\nRecommended value: Default
 EditorParameterPage.ModifyOrder=Modify Order:
 EditorParameterPage.ModifyOrderAddFirst=ADD First
 EditorParameterPage.ModifyOrderDelFirst=DELETE First
diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java
index af5b4fa..2cc7bad 100644
--- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java
+++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java
@@ -30,6 +30,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -406,7 +407,7 @@ public class Utils
         ModifyOrder modifyAddDeleteOrder = oldEntry.getBrowserConnection().getModifyAddDeleteOrder();
 
         // get all attribute descriptions
-        Set<String> attributeDescriptions = new HashSet<>();
+        Set<String> attributeDescriptions = new LinkedHashSet<>();
         
         for ( IAttribute oldAttr : oldEntry.getAttributes() )
         {
@@ -487,6 +488,10 @@ public class Utils
                 {
                     // delete all
                     modSpec = LdifModSpec.createDelete( attributeDescription );
+                    for ( IValue value : oldAttribute.getValues() )
+                    {
+                        modSpec.addAttrVal( computeDiffCreateAttrValLine( value ) );
+                    }
                 }
                 
                 modSpec.finish( LdifModSpecSepLine.create() );
@@ -558,16 +563,12 @@ public class Utils
                     /*
                      *  we use add/del in the following cases:
                      *  - add/del is forced in the connection configuration
-                     *  - only values to add
-                     *  - only values to delete
-                     *  - the sum of adds and deletes is smaller or equal than the number of replaces
+                     *  - for attributes w/o X-ORDERED 'VALUES'
                      *  
                      *  we use replace in the following cases:
-                     *  - the number of replaces is smaller to the sum of adds and deletes
                      *  - for attributes with X-ORDERED 'VALUES'
                      */
-                    if ( isAddDelForced || ( toAdd.size() + toDel.size() <= newAttrValLines.size() && !isOrderedValue )
-                        || ( !toDel.isEmpty() && toAdd.isEmpty() ) || ( !toAdd.isEmpty() && toDel.isEmpty() ) )
+                    if ( isAddDelForced || !isOrderedValue )
                     {
                         // add/del del/add
                         LdifModSpec addModSpec = LdifModSpec.createAdd( attributeDescription );
diff --git a/tests/test.integration.core/pom-first.xml b/tests/test.integration.core/pom-first.xml
index 70a38f9..9a1a9ad 100644
--- a/tests/test.integration.core/pom-first.xml
+++ b/tests/test.integration.core/pom-first.xml
@@ -48,6 +48,7 @@
             <Bundle-Activator>org.apache.directory.studio.test.integration.core.Activator</Bundle-Activator>
             
             <Require-Bundle>org.junit;bundle-version="4.11.0",
+ org.hamcrest.library;bundle-version="1.3.0",
  org.apache.directory.server.apacheds-test-framework;bundle-version="${org.apache.directory.server.version}",
  org.apache.directory.server.annotations;bundle-version="${org.apache.directory.server.version}",
  org.apache.directory.server.xdbm.partition;bundle-version="${org.apache.directory.server.version}",
diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/ComputeDiffTest.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/ComputeDiffTest.java
new file mode 100644
index 0000000..1f7cc8e
--- /dev/null
+++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/ComputeDiffTest.java
@@ -0,0 +1,309 @@
+/*
+ *  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.core;
+
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertNull;
+
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.studio.connection.core.event.ConnectionEventRegistry;
+import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
+import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection.ModifyMode;
+import org.apache.directory.studio.ldapbrowser.core.model.IEntry;
+import org.apache.directory.studio.ldapbrowser.core.model.impl.Attribute;
+import org.apache.directory.studio.ldapbrowser.core.model.impl.DummyConnection;
+import org.apache.directory.studio.ldapbrowser.core.model.impl.DummyEntry;
+import org.apache.directory.studio.ldapbrowser.core.model.impl.Value;
+import org.apache.directory.studio.ldapbrowser.core.model.schema.Schema;
+import org.apache.directory.studio.ldapbrowser.core.utils.Utils;
+import org.apache.directory.studio.ldifparser.LdifParserConstants;
+import org.apache.directory.studio.ldifparser.model.LdifFile;
+import org.apache.directory.studio.ldifparser.model.container.LdifChangeModifyRecord;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class ComputeDiffTest
+{
+    private IBrowserConnection connection;
+
+    private IEntry oldEntry;
+    private IEntry newEntry;
+
+    static class TestConnection extends DummyConnection
+    {
+        private static final long serialVersionUID = 1L;
+
+        private ModifyMode modifyMode = ModifyMode.DEFAULT;
+        private ModifyMode modifyModeNoEMR = ModifyMode.DEFAULT;
+
+        public TestConnection()
+        {
+            super( Schema.DEFAULT_SCHEMA );
+        }
+
+
+        public ModifyMode getModifyMode()
+        {
+            return modifyMode;
+        }
+
+
+        public void setModifyMode( ModifyMode mode )
+        {
+            this.modifyMode = mode;
+        }
+
+
+        public ModifyMode getModifyModeNoEMR()
+        {
+            return modifyModeNoEMR;
+        }
+
+
+        public void setModifyModeNoEMR( ModifyMode mode )
+        {
+            this.modifyModeNoEMR = mode;
+        }
+
+    }
+
+    @Before
+    public void setup() throws Exception
+    {
+        ConnectionEventRegistry.suspendEventFiringInCurrentThread();
+        connection = new TestConnection();
+        oldEntry = new DummyEntry( new Dn( "cn=foo" ), connection );
+        newEntry = new DummyEntry( new Dn( "cn=foo" ), connection );
+    }
+
+
+    @Test
+    public void shouldReturnNullForEqualEntries()
+    {
+        // entries without attribute
+        assertNull( Utils.computeDiff( oldEntry, newEntry ) );
+        assertNull( Utils.computeDiff( oldEntry, oldEntry ) );
+        assertNull( Utils.computeDiff( newEntry, newEntry ) );
+
+        // entries with attributes
+        addAttribute( oldEntry, "cn", "1" );
+        addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3" );
+        addAttribute( newEntry, "cn", "1" );
+        addAttribute( newEntry, "member", "cn=1", "cn=2", "cn=3" );
+        assertNull( Utils.computeDiff( oldEntry, newEntry ) );
+        assertNull( Utils.computeDiff( oldEntry, oldEntry ) );
+        assertNull( Utils.computeDiff( newEntry, newEntry ) );
+    }
+
+
+    @Test
+    public void shouldAddOneAttributeWithOneValue()
+    {
+        addAttribute( newEntry, "cn", "1" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "add:cn", "cn:1" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "cn:1" );
+    }
+
+
+    @Test
+    public void shouldAddMultipleAttributeWithMultipleValues()
+    {
+        addAttribute( newEntry, "cn", "1", "2" );
+        addAttribute( newEntry, "member", "cn=1", "cn=2", "cn=3" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "add:cn", "cn:1", "cn:2", "-", "add:member", "member:cn=1", "member:cn=2", "member:cn=3" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "replace:cn", "cn:1", "cn:2", "-", "replace:member", "member:cn=1", "member:cn=2", "member:cn=3" );
+    }
+
+
+    @Test
+    public void shouldAddOneValueToOneExistingAttribute()
+    {
+        addAttribute( oldEntry, "cn", "1" );
+        addAttribute( newEntry, "cn", "1", "2" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "add:cn", "cn:2" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "cn:1", "cn:2" );
+    }
+
+
+    @Test
+    public void shouldAddMultipleValuesToMultipleExistingAttributes()
+    {
+        addAttribute( oldEntry, "cn", "1" );
+        addAttribute( newEntry, "cn", "1", "2", "3" );
+        addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3" );
+        addAttribute( newEntry, "member", "cn=1", "cn=2", "cn=3", "cn=4", "cn=5" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "add:cn", "cn:2", "cn:3", "-", "add:member", "member:cn=4", "member:cn=5" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "replace:cn", "cn:1", "cn:2", "cn:3", "-",
+            "replace:member", "member:cn=1", "member:cn=2", "member:cn=3", "member:cn=4", "member:cn=5" );
+    }
+
+
+    @Test
+    public void shouldDeleteAllOneValue()
+    {
+        addAttribute( oldEntry, "cn", "1" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "delete:cn", "cn:1" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn" );
+    }
+
+
+    @Test
+    public void shouldDeleteAllMultipleValues()
+    {
+        addAttribute( oldEntry, "cn", "1", "2" );
+        addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "delete:cn", "cn:1", "cn:2", "-",
+            "delete:member", "member:cn=1", "member:cn=2", "member:cn=3" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "-", "replace:member" );
+    }
+
+
+    @Test
+    public void shouldDeleteOneValue()
+    {
+        addAttribute( oldEntry, "cn", "1", "2", "3" );
+        addAttribute( newEntry, "cn", "1", "2" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "delete:cn", "cn:3" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "cn:1", "cn:2" );
+    }
+
+
+    @Test
+    public void shouldDeleteMultipleValues()
+    {
+        addAttribute( oldEntry, "cn", "1", "2", "3" );
+        addAttribute( newEntry, "cn", "1" );
+        addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3", "cn=4", "cn=5" );
+        addAttribute( newEntry, "member", "cn=1" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "delete:cn", "cn:2", "cn:3", "-",
+            "delete:member", "member:cn=2", "member:cn=3", "member:cn=4", "member:cn=5" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "replace:cn", "cn:1", "-", "replace:member", "member:cn=1" );
+    }
+
+
+    @Test
+    public void shouldReplaceOneValue()
+    {
+        addAttribute( oldEntry, "cn", "1" );
+        addAttribute( newEntry, "cn", "2" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "delete:cn", "cn:1", "-", "add:cn", "cn:2" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "cn:2" );
+    }
+
+
+    @Test
+    public void shouldReplaceMultipleValues()
+    {
+        addAttribute( oldEntry, "cn", "1", "2", "3" );
+        addAttribute( newEntry, "cn", "4" );
+        addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3" );
+        addAttribute( newEntry, "member", "cn=1", "cn=4", "cn=5" );
+
+        connection.setModifyMode( ModifyMode.DEFAULT );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "delete:cn", "cn:1", "cn:2", "cn:3", "-", "add:cn", "cn:4", "-",
+            "delete:member", "member:cn=2", "member:cn=3", "-", "add:member", "member:cn=4", "member:cn=5" );
+
+        connection.setModifyMode( ModifyMode.REPLACE );
+        assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+            "replace:cn", "cn:4", "-",
+            "replace:member", "member:cn=1", "member:cn=4", "member:cn=5" );
+    }
+
+
+    private static void addAttribute( IEntry entry, String attributeName, Object... rawValues )
+    {
+        Attribute attribute = new Attribute( entry, attributeName );
+        entry.addAttribute( attribute );
+        for ( Object rawValue : rawValues )
+        {
+            Value value = new Value( attribute, rawValue );
+            attribute.addValue( value );
+        }
+    }
+
+
+    private void assertChangeModify( LdifFile diff, String... lines )
+    {
+        // System.out.println( diff.toRawString() );
+        assertThat( diff.isChangeType(), equalTo( true ) );
+        assertThat( diff.getContainers(), hasSize( 1 ) );
+        assertThat( diff.getLastContainer(), instanceOf( LdifChangeModifyRecord.class ) );
+
+        String s = "changetype:modify" + LdifParserConstants.LINE_SEPARATOR;
+        for ( String line : lines )
+        {
+            assertThat( diff.toRawString(), containsString( line ) );
+            s += line + LdifParserConstants.LINE_SEPARATOR;
+        }
+        s += "-" + LdifParserConstants.LINE_SEPARATOR;
+        assertThat( diff.toRawString(), containsString( s ) );
+    }
+
+}
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/EntryEditorTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/EntryEditorTest.java
index ae33a71..04fcbec 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/EntryEditorTest.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/EntryEditorTest.java
@@ -125,9 +125,6 @@ public class EntryEditorTest extends AbstractLdapTestUnit
 
     /**
      * Test adding, editing and deleting of attributes in the entry editor.
-     *
-     * @throws Exception
-     *             the exception
      */
     @Test
     public void testAddEditDeleteAttribute() throws Exception
@@ -165,7 +162,6 @@ public class EntryEditorTest extends AbstractLdapTestUnit
         entryEditorBot.editValue( "description", "This is the 2nd description." );
         entryEditorBot.typeValueAndFinish( "This is the 3rd description." );
         assertEquals( 10, entryEditorBot.getAttributeValues().size() );
-        assertEquals( 10, entryEditorBot.getAttributeValues().size() );
         assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
         assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 2nd description." ) );
         assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 3rd description." ) );
@@ -185,13 +181,14 @@ public class EntryEditorTest extends AbstractLdapTestUnit
         assertEquals( 9, entryEditorBot.getAttributeValues().size() );
         assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
         assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
-        modificationLogsViewBot.waitForText( "replace: description\ndescription: This is the final description." );
+        modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 1st description." );
+        modificationLogsViewBot.waitForText( "add: description\ndescription: This is the final description." );
 
         // delete 1st value/attribute
         entryEditorBot.deleteValue( "description", "This is the final description." );
         assertEquals( 8, entryEditorBot.getAttributeValues().size() );
         assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
-        modificationLogsViewBot.waitForText( "delete: description\n-" );
+        modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the final description.\n-" );
 
         assertEquals( "Expected 6 modifications.", 6,
             StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
@@ -199,6 +196,55 @@ public class EntryEditorTest extends AbstractLdapTestUnit
 
 
     /**
+     * Test adding, editing and deleting of attributes without equality matching rule in the entry editor.
+     */
+    @Test
+    public void testAddEditDeleteAttributeWithoutEqualityMatchingRule() throws Exception
+    {
+        browserViewBot.selectEntry( "DIT", "Root DSE", "ou=system", "ou=users", "cn=Barbara Jensen" );
+
+        EntryEditorBot entryEditorBot = studioBot.getEntryEditorBot( "cn=Barbara Jensen,ou=users,ou=system" );
+        entryEditorBot.activate();
+        String dn = entryEditorBot.getDnText();
+        assertEquals( "DN: cn=Barbara Jensen,ou=users,ou=system", dn );
+        assertEquals( 8, entryEditorBot.getAttributeValues().size() );
+        assertEquals( "", modificationLogsViewBot.getModificationLogsText() );
+
+        // add facsimileTelephoneNumber attribute
+        entryEditorBot.activate();
+        NewAttributeWizardBot wizardBot = entryEditorBot.openNewAttributeWizard();
+        assertTrue( wizardBot.isVisible() );
+        wizardBot.typeAttributeType( "facsimileTelephoneNumber" );
+        wizardBot.clickFinishButton();
+        entryEditorBot.typeValueAndFinish( "+1 234 567 890" );
+        assertEquals( 9, entryEditorBot.getAttributeValues().size() );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+        modificationLogsViewBot
+            .waitForText( "add: facsimileTelephoneNumber\nfacsimileTelephoneNumber: +1 234 567 890" );
+
+        // edit value
+        entryEditorBot.editValue( "facsimileTelephoneNumber", "+1 234 567 890" );
+        entryEditorBot.typeValueAndFinish( "000000000000" );
+        assertEquals( 9, entryEditorBot.getAttributeValues().size() );
+        assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+        modificationLogsViewBot
+            .waitForText( "delete: facsimileTelephoneNumber\nfacsimileTelephoneNumber: +1 234 567 890" );
+        modificationLogsViewBot.waitForText( "add: facsimileTelephoneNumber\nfacsimileTelephoneNumber: 000000000000" );
+
+        // delete 1st value/attribute
+        entryEditorBot.deleteValue( "facsimileTelephoneNumber", "000000000000" );
+        assertEquals( 8, entryEditorBot.getAttributeValues().size() );
+        assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+        modificationLogsViewBot
+            .waitForText( "delete: facsimileTelephoneNumber\nfacsimileTelephoneNumber: 000000000000\n-" );
+
+        assertEquals( "Expected 3 modifications.", 3,
+            StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
+    }
+
+
+    /**
      * DIRSTUDIO-483: DN Editor escapes all non-ascii characters
      *
      * @throws Exception
@@ -471,7 +517,8 @@ public class EntryEditorTest extends AbstractLdapTestUnit
         assertTrue( entryEditorBot.getAttributeValues().contains( "description: " + newValue ) );
         String description2Ldif = LdifAttrValLine.create( "description", newValue )
             .toFormattedString( LdifFormatParameters.DEFAULT ).replace( LdifParserConstants.LINE_SEPARATOR, "\n" );
-        modificationLogsViewBot.waitForText( "replace: description\n" + description2Ldif );
+        modificationLogsViewBot.waitForText( "delete: description\ndescription: testTextValueEditor 1" );
+        modificationLogsViewBot.waitForText( "add: description\n" + description2Ldif );
     }
 
 
@@ -639,7 +686,8 @@ public class EntryEditorTest extends AbstractLdapTestUnit
         aciItemEditor.clickOkButton();
 
         SWTUtils.sleep( 1000 );
-        modificationLogsViewBot.waitForText( "replace: entryaci\n" );
+        modificationLogsViewBot.waitForText( "delete: entryaci\n" );
+        modificationLogsViewBot.waitForText( "add: entryaci\n" );
     }
 
 
@@ -678,7 +726,8 @@ public class EntryEditorTest extends AbstractLdapTestUnit
         aciItemEditor.clickOkButton();
 
         SWTUtils.sleep( 1000 );
-        modificationLogsViewBot.waitForText( "replace: entryaci\n" );
+        modificationLogsViewBot.waitForText( "delete: entryaci\n" );
+        modificationLogsViewBot.waitForText( "add: entryaci\n" );
     }
 
 
@@ -706,7 +755,8 @@ public class EntryEditorTest extends AbstractLdapTestUnit
         aciItemEditor.clickOkButton();
 
         SWTUtils.sleep( 1000 );
-        modificationLogsViewBot.waitForText( "replace: prescriptiveaci\n" );
+        modificationLogsViewBot.waitForText( "delete: prescriptiveaci\n" );
+        modificationLogsViewBot.waitForText( "add: prescriptiveaci\n" );
     }
 
 
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ErrorHandlingTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ErrorHandlingTest.java
index 0f3dd30..6b302a8 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ErrorHandlingTest.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ErrorHandlingTest.java
@@ -195,7 +195,8 @@ public class ErrorHandlingTest extends AbstractLdapTestUnit
 
         // verify in modification logs
         modificationLogsViewBot.assertContainsError( "[LDAP result code 21 - invalidAttributeSyntax]",
-            "dn: uid=user.1,ou=users,ou=system", "changetype: modify", "replace: telephonenumber",
+            "dn: uid=user.1,ou=users,ou=system", "changetype: modify", "delete: telephonenumber",
+            "telephonenumber: 976-893-3312", "-", "add: telephonenumber",
             "telephonenumber: Invalid phone number" );
     }
 
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
index 72d94f4..0eae6d1 100644
--- 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
@@ -24,6 +24,7 @@ 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.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -33,15 +34,21 @@ import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 
+import org.apache.commons.lang3.StringUtils;
 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.BrowserCorePlugin;
 import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
+import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection.ModifyMode;
 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.EntryEditorBot;
+import org.apache.directory.studio.test.integration.ui.bots.ModificationLogsViewBot;
+import org.apache.directory.studio.test.integration.ui.bots.NewAttributeWizardBot;
 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;
@@ -101,6 +108,7 @@ public class OpenLdapTest
     private StudioBot studioBot;
     private ConnectionsViewBot connectionsViewBot;
     private BrowserViewBot browserViewBot;
+    private ModificationLogsViewBot modificationLogsViewBot;
 
     private Connection connection;
 
@@ -113,6 +121,7 @@ public class OpenLdapTest
         connection = connectionsViewBot.createTestConnection( "OpenLdapTest", OPENLDAP_HOST, OPENLDAP_PORT,
             OPENLDAP_ADMIN_DN, OPENLDAP_ADMIN_PASSWORD );
         browserViewBot = studioBot.getBrowserView();
+        modificationLogsViewBot = studioBot.getModificationLogsViewBot();
 
         try ( LdapNetworkConnection connection = new LdapNetworkConnection( OPENLDAP_HOST, OPENLDAP_PORT );
             LdifReader ldifReader = new LdifReader( OpenLdapTest.class.getResourceAsStream( "OpenLdapTest.ldif" ) ) )
@@ -293,4 +302,128 @@ public class OpenLdapTest
         wizardBot.clickCancelButton();
     }
 
+
+    /**
+     * Test adding, editing and deleting of attributes in the entry editor.
+     */
+    @Test
+    public void testAddEditDeleteAttribute() throws Exception
+    {
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.1" );
+
+        EntryEditorBot entryEditorBot = studioBot.getEntryEditorBot( "uid=user.1,ou=users,dc=example,dc=org" );
+        entryEditorBot.activate();
+        String dn = entryEditorBot.getDnText();
+        assertEquals( "DN: uid=user.1,ou=users,dc=example,dc=org", dn );
+        assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+        assertEquals( "", modificationLogsViewBot.getModificationLogsText() );
+
+        // add description attribute
+        entryEditorBot.activate();
+        NewAttributeWizardBot wizardBot = entryEditorBot.openNewAttributeWizard();
+        assertTrue( wizardBot.isVisible() );
+        wizardBot.typeAttributeType( "description" );
+        wizardBot.clickFinishButton();
+        entryEditorBot.typeValueAndFinish( "This is the 1st description." );
+        assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+        modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 1st description." );
+
+        // add second value
+        entryEditorBot.activate();
+        entryEditorBot.addValue( "description" );
+        entryEditorBot.typeValueAndFinish( "This is the 2nd description." );
+        assertEquals( 24, entryEditorBot.getAttributeValues().size() );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 2nd description." ) );
+        modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 2nd description." );
+
+        // edit second value
+        entryEditorBot.editValue( "description", "This is the 2nd description." );
+        entryEditorBot.typeValueAndFinish( "This is the 3rd description." );
+        assertEquals( 24, entryEditorBot.getAttributeValues().size() );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+        assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 2nd description." ) );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 3rd description." ) );
+        modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 2nd description." );
+        modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 3rd description." );
+
+        // delete second value
+        entryEditorBot.deleteValue( "description", "This is the 3rd description." );
+        assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+        assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 3rd description." ) );
+        modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 3rd description." );
+
+        // edit 1st value
+        entryEditorBot.editValue( "description", "This is the 1st description." );
+        entryEditorBot.typeValueAndFinish( "This is the final description." );
+        assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+        assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
+        modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 1st description." );
+        modificationLogsViewBot.waitForText( "add: description\ndescription: This is the final description." );
+
+        // delete 1st value/attribute
+        entryEditorBot.deleteValue( "description", "This is the final description." );
+        assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+        assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
+        modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the final description.\n-" );
+
+        assertEquals( "Expected 6 modifications.", 6,
+            StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
+    }
+
+
+    /**
+     * Test adding, editing and deleting of attributes without equality matching rule in the entry editor.
+     */
+    @Test
+    public void testAddEditDeleteAttributeWithoutEqualityMatchingRule() throws Exception
+    {
+        IBrowserConnection browserConnection = BrowserCorePlugin.getDefault().getConnectionManager()
+            .getBrowserConnection( connection );
+        browserConnection.setModifyModeNoEMR( ModifyMode.REPLACE );
+
+        browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.1" );
+
+        EntryEditorBot entryEditorBot = studioBot.getEntryEditorBot( "uid=user.1,ou=users,dc=example,dc=org" );
+        entryEditorBot.activate();
+        String dn = entryEditorBot.getDnText();
+        assertEquals( "DN: uid=user.1,ou=users,dc=example,dc=org", dn );
+        assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+        assertEquals( "", modificationLogsViewBot.getModificationLogsText() );
+
+        // add facsimileTelephoneNumber attribute
+        entryEditorBot.activate();
+        NewAttributeWizardBot wizardBot = entryEditorBot.openNewAttributeWizard();
+        assertTrue( wizardBot.isVisible() );
+        wizardBot.typeAttributeType( "facsimileTelephoneNumber" );
+        wizardBot.clickFinishButton();
+        entryEditorBot.typeValueAndFinish( "+1 234 567 890" );
+        assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+        modificationLogsViewBot
+            .waitForText( "replace: facsimileTelephoneNumber\nfacsimileTelephoneNumber: +1 234 567 890" );
+
+        // edit value
+        entryEditorBot.editValue( "facsimileTelephoneNumber", "+1 234 567 890" );
+        entryEditorBot.typeValueAndFinish( "000000000000" );
+        assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+        assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+        assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+        modificationLogsViewBot
+            .waitForText( "replace: facsimileTelephoneNumber\nfacsimileTelephoneNumber: 000000000000" );
+
+        // delete 1st value/attribute
+        entryEditorBot.deleteValue( "facsimileTelephoneNumber", "000000000000" );
+        assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+        assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+        modificationLogsViewBot
+            .waitForText( "replace: facsimileTelephoneNumber\n-" );
+
+        assertEquals( "Expected 3 modifications.", 3,
+            StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
+    }
+
 }
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/AbstractLogsViewBot.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/AbstractLogsViewBot.java
index 138809e..b879f5a 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/AbstractLogsViewBot.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/AbstractLogsViewBot.java
@@ -20,6 +20,7 @@
 package org.apache.directory.studio.test.integration.ui.bots;
 
 
+import org.apache.commons.lang3.StringUtils;
 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
 import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
 import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
@@ -64,7 +65,7 @@ public class AbstractLogsViewBot
                 {
                     refreshButton.click();
                 }
-                return view.bot().styledText().getText().contains( text );
+                return StringUtils.containsIgnoreCase( view.bot().styledText().getText(), text );
             }
 
 
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/EntryEditorWidgetBot.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/EntryEditorWidgetBot.java
index 61e1bbf..70bbecc 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/EntryEditorWidgetBot.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/EntryEditorWidgetBot.java
@@ -191,7 +191,8 @@ class EntryEditorWidgetBot
         SWTBotTreeItem[] allItems = tree.getAllItems();
         for ( SWTBotTreeItem item : allItems )
         {
-            if ( item.cell( 0 ).equals( attributeType ) && ( value == null || item.cell( 1 ).equals( value ) ) )
+            if ( item.cell( 0 ).equalsIgnoreCase( attributeType )
+                && ( value == null || item.cell( 1 ).equals( value ) ) )
             {
                 return item;
             }
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
index d64ed89..4e39b2f 100644
--- 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
@@ -42,7 +42,6 @@ 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