Bug 533827 - Infinite loop in SettingToControlMapperImpl
Based on the fix provided by Remi (https://git.eclipse.org/r/#/c/121455/
).
This includes tests for the infinitive loop as well as the missing
removal of the parent from all setting.
Change-Id: I78609be11cf5dd389977279ceb455f7c0d65188a
Signed-off-by: Eugen Neufeld <eneufeld@eclipsesource.com>
diff --git a/bundles/org.eclipse.emfforms.core.services/src/org/eclipse/emfforms/internal/core/services/controlmapper/SettingToControlMapperImpl.java b/bundles/org.eclipse.emfforms.core.services/src/org/eclipse/emfforms/internal/core/services/controlmapper/SettingToControlMapperImpl.java
index 6ea3e5b..c21dccd 100644
--- a/bundles/org.eclipse.emfforms.core.services/src/org/eclipse/emfforms/internal/core/services/controlmapper/SettingToControlMapperImpl.java
+++ b/bundles/org.eclipse.emfforms.core.services/src/org/eclipse/emfforms/internal/core/services/controlmapper/SettingToControlMapperImpl.java
@@ -373,18 +373,20 @@
* @param controlToRemove The {@link VControl} that will be removed from this setting to control mapper
*/
private void vControlParentsRemoved(EMFFormsViewContext childContext, VControl controlToRemove) {
- VElement parentElement = contextParentMap.get(childContext);
- while (parentElement != null) {
- for (final UniqueSetting setting : controlToSettingMap.get(controlToRemove)) {
- settingToControlMap.get(setting).remove(parentElement);
- final EMFFormsViewContext context = controlContextMap.get(parentElement);
- if (context == null) {
- parentElement = null;
- break;
- }
- parentElement = contextParentMap.get(context);
- }
+ if (childContext == null) {
+ return;
}
+ final VElement parentElement = contextParentMap.get(childContext);
+ if (parentElement == null) {
+ return;
+ }
+ // remove the mapping of each setting of the removed control to the parent element
+ for (final UniqueSetting setting : controlToSettingMap.get(controlToRemove)) {
+ settingToControlMap.get(setting).remove(parentElement);
+ }
+ // Then compute the parent of the parent element
+ final EMFFormsViewContext parentContext = controlContextMap.get(parentElement);
+ vControlParentsRemoved(parentContext, controlToRemove);
}
/**
diff --git a/tests/org.eclipse.emfforms.core.services.tests/src/org/eclipse/emfforms/internal/core/services/scoped/SettingToControlMapper_ITest.java b/tests/org.eclipse.emfforms.core.services.tests/src/org/eclipse/emfforms/internal/core/services/scoped/SettingToControlMapper_ITest.java
index 246c24a..848a5af 100644
--- a/tests/org.eclipse.emfforms.core.services.tests/src/org/eclipse/emfforms/internal/core/services/scoped/SettingToControlMapper_ITest.java
+++ b/tests/org.eclipse.emfforms.core.services.tests/src/org/eclipse/emfforms/internal/core/services/scoped/SettingToControlMapper_ITest.java
@@ -13,6 +13,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -39,6 +43,8 @@
import org.eclipse.emfforms.spi.core.services.mappingprovider.EMFFormsMappingProviderManager;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
public class SettingToControlMapper_ITest {
@@ -49,6 +55,7 @@
private EObject domainObject;
private VControl control;
private FakeViewContext context;
+ private EMFFormsMappingProviderManager mappingManager;
@Before
public void setup() {
@@ -58,17 +65,25 @@
view.getChildren().add(control);
domainObject = EcoreUtil.create(EcorePackage.eINSTANCE.getEClass());
context = new FakeViewContext(domainObject, view);
- mapper = new SettingToControlMapperImpl(
- new EMFFormsMappingProviderManager() {
+
+ mappingManager = mock(EMFFormsMappingProviderManager.class);
+ when(mappingManager.getAllSettingsFor(any(VDomainModelReference.class), any(EObject.class)))
+ .then(new Answer<Set<UniqueSetting>>() {
+
@Override
- public Set<UniqueSetting> getAllSettingsFor(VDomainModelReference domainModelReference,
- EObject domainObject) {
+ public Set<UniqueSetting> answer(InvocationOnMock invocation) throws Throwable {
final Set<UniqueSetting> settings = new LinkedHashSet<UniqueSetting>();
+ final Object[] arguments = invocation.getArguments();
settings.add(
- UniqueSetting.createSetting(domainObject, EcorePackage.eINSTANCE.getEClass_EAttributes()));
+ UniqueSetting.createSetting((EObject) arguments[1],
+ EcorePackage.eINSTANCE.getEClass_EAttributes()));
+ settings.add(
+ UniqueSetting.createSetting((EObject) arguments[1],
+ EcorePackage.eINSTANCE.getEClass_EReferences()));
return settings;
}
- }, context);
+ });
+ mapper = new SettingToControlMapperImpl(mappingManager, context);
}
@Test
@@ -154,27 +169,31 @@
final FakeViewContext childContext = new FakeViewContext(childDomainObject, childView);
context.addChildContext(control, childContext);
+ final UniqueSetting childSettingAttributes = UniqueSetting.createSetting(childDomainObject,
+ EcorePackage.eINSTANCE.getEClass_EAttributes());
+ final UniqueSetting childSettingReferences = UniqueSetting.createSetting(childDomainObject,
+ EcorePackage.eINSTANCE.getEClass_EReferences());
+ assertEquals(2, mapper.getControlsFor(childSettingAttributes).size());
+ assertEquals(2, mapper.getControlsFor(childSettingReferences).size());
+
// Verification that add control and add child context work is done by other test cases.
mapper.childContextDisposed(childContext);
- final UniqueSetting setting = UniqueSetting.createSetting(childDomainObject,
- EcorePackage.eINSTANCE.getEClass_EAttributes());
-
// Verify that the mapping for the child setting is empty
final Set<UniqueSetting> settingsForControl = mapper.getSettingsForControl(childControl);
assertEquals(0, settingsForControl.size());
// Verify that after disposing the child context, the mapping from setting to parent control is also removed
- final Set<VElement> controlsForSetting = mapper.getControlsFor(setting);
- assertEquals(0, controlsForSetting.size());
+ assertEquals(0, mapper.getControlsFor(childSettingAttributes).size());
+ assertEquals(0, mapper.getControlsFor(childSettingReferences).size());
final UniqueSetting parentSetting = UniqueSetting.createSetting(domainObject,
EcorePackage.eINSTANCE.getEClass_EAttributes());
// Verify that disposing a child context does not clear the mapping of the parent's control
final Set<UniqueSetting> settingsForParentControl = mapper.getSettingsForControl(control);
- assertEquals(1, settingsForParentControl.size());
+ assertEquals(2, settingsForParentControl.size());
assertTrue(settingsForParentControl.contains(parentSetting));
// Verify that disposing a child context does not clear the mapping of the parent's setting
@@ -242,4 +261,40 @@
// half a millisecond should be more then enough time
assertTrue(MessageFormat.format("Duration was {0}.", durationYs), durationYs < 500); //$NON-NLS-1$
}
+
+ @Test
+ public void childContextDisposed_533827_InfiniteLoop() {
+ // Add parent control to later verify that it is not illegally removed during child context disposal
+ mapper.vControlAdded(control);
+
+ // Setup and add child context with one control and one setting
+ final VView childView = VViewFactory.eINSTANCE.createView();
+ final VControl childControl = VViewFactory.eINSTANCE.createControl();
+ childControl.setDomainModelReference(EcorePackage.eINSTANCE.getEClass_EAttributes());
+ childView.getChildren().add(childControl);
+ final EClass childDomainObject = EcoreFactory.eINSTANCE.createEClass();
+ childDomainObject.setName("child"); //$NON-NLS-1$
+ final Set<UniqueSetting> uniqueSettings = Collections.emptySet();
+ when(mappingManager.getAllSettingsFor(same(childControl.getDomainModelReference()), any(EObject.class)))
+ .thenReturn(uniqueSettings);
+ final FakeViewContext childContext = new FakeViewContext(childDomainObject, childView);
+ context.addChildContext(control, childContext);
+
+ // Verification that add control and add child context work is done by other test cases.
+ // mapper.vControlRemoved(childControl);
+ mapper.childContextDisposed(childContext);
+
+ final UniqueSetting parentSetting = UniqueSetting.createSetting(domainObject,
+ EcorePackage.eINSTANCE.getEClass_EAttributes());
+
+ // Verify that disposing a child context does not clear the mapping of the parent's control
+ final Set<UniqueSetting> settingsForParentControl = mapper.getSettingsForControl(control);
+ assertEquals(2, settingsForParentControl.size());
+ assertTrue(settingsForParentControl.contains(parentSetting));
+
+ // Verify that disposing a child context does not clear the mapping of the parent's setting
+ final Set<VElement> controlsForParentSetting = mapper.getControlsFor(parentSetting);
+ assertEquals(1, controlsForParentSetting.size());
+ assertTrue(controlsForParentSetting.contains(control));
+ }
}