Bug 547422 - Fix i18n support and available values for Enum Renderer
Fix exception when model changed on a background thread that
causes the UI not to be updated.
Change-Id: Icb6aad8f42ca9f2c67b92e081b1eb530401a4049
Signed-off-by: Christian W. Damus <give.a.damus@gmail.com>
diff --git a/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/internal/core/swt/renderer/EnumComboViewerSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/internal/core/swt/renderer/EnumComboViewerSWTRenderer.java
index 329f383..915c5e7 100644
--- a/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/internal/core/swt/renderer/EnumComboViewerSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/internal/core/swt/renderer/EnumComboViewerSWTRenderer.java
@@ -12,6 +12,7 @@
* Eugen - initial API and implementation
* Christian Damus - enum choice filtering based on ItemPropertyDescriptor
* Lucas Koehler - enum choice filtering based on ItemPropertyDescriptor
+ * Christian W. Damus - bug 547422
******************************************************************************/
package org.eclipse.emf.ecp.view.internal.core.swt.renderer;
@@ -207,7 +208,7 @@
availableChoicesValue = new ComputedValue<Collection<?>>(Collection.class) {
private final Optional<IChangeNotifier> changeNotifier = propertySource
.filter(IChangeNotifier.class::isInstance).map(IChangeNotifier.class::cast);
- private final INotifyChangedListener listener = __ -> makeDirty();
+ private final INotifyChangedListener listener = __ -> getRealm().exec(this::makeDirty);
{
changeNotifier.ifPresent(cn -> cn.addListener(listener));
diff --git a/tests/org.eclipse.emf.ecp.view.core.swt.tests/src/org/eclipse/emf/ecp/view/internal/core/swt/renderer/EnumComboViewerRenderer_PTest.java b/tests/org.eclipse.emf.ecp.view.core.swt.tests/src/org/eclipse/emf/ecp/view/internal/core/swt/renderer/EnumComboViewerRenderer_PTest.java
index 38918c5..cd0a6d0 100644
--- a/tests/org.eclipse.emf.ecp.view.core.swt.tests/src/org/eclipse/emf/ecp/view/internal/core/swt/renderer/EnumComboViewerRenderer_PTest.java
+++ b/tests/org.eclipse.emf.ecp.view.core.swt.tests/src/org/eclipse/emf/ecp/view/internal/core/swt/renderer/EnumComboViewerRenderer_PTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011-2015 EclipseSource Muenchen GmbH and others.
+ * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -10,19 +10,31 @@
*
* Contributors:
* Lucas Koehler - initial API and implementation
+ * Christian W. Damus - bug 547422
******************************************************************************/
package org.eclipse.emf.ecp.view.internal.core.swt.renderer;
+import static org.hamcrest.CoreMatchers.anything;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.property.Properties;
import org.eclipse.emf.databinding.EMFProperties;
@@ -50,9 +62,11 @@
import org.eclipse.emfforms.spi.core.services.label.NoLabelFoundException;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
import org.eclipse.emfforms.swt.common.test.AbstractControl_PTest;
+import org.eclipse.jface.databinding.swt.DisplayRealm;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.junit.After;
import org.junit.Before;
@@ -237,4 +251,62 @@
assertSame(TestEnum.B, iterator.next());
assertSame(TestEnum.C, iterator.next());
}
+
+ /**
+ * Verify that the renderer correctly updates the UI when the model is updated directly
+ * as by updating with change sets from an EMFStore repository.
+ */
+ @SuppressWarnings("nls")
+ @Test
+ public void updateUIFromModelChange() {
+ // Have to run this test in a real SWT Display realm because the DefaultRealm for testing
+ // doesn't enforce the thread on which things happen in the observables
+ final Realm realRealm = DisplayRealm.getRealm(Display.getCurrent());
+ Realm.runWithDefault(realRealm, () -> {
+ try {
+ when(getDatabindingService().getObservableValue(any(VDomainModelReference.class), any(EObject.class)))
+ .thenReturn(observableValue);
+
+ // A is filtered by annotation and D is filtered by the property descriptor,
+ // so we can only use B and C for testing
+ domainObject.setMyEnum(TestEnum.B);
+ renderControl(new SWTGridCell(0, 2, getRenderer()));
+
+ final EnumComboViewerSWTRenderer enumRenderer = (EnumComboViewerSWTRenderer) getRenderer();
+ final IObservableValue<?> availableChoices = enumRenderer.getAvailableChoicesValue();
+
+ final IChangeListener listener = mock(IChangeListener.class);
+ availableChoices.addChangeListener(listener);
+
+ // Make changes to the model on background threads and verify that
+ // the observable machinery works correctly
+ final TestEnum[] valuesToSet = { TestEnum.C, TestEnum.B };
+ final List<Throwable> thrown = new ArrayList<>(valuesToSet.length);
+ for (final TestEnum valueToSet : valuesToSet) {
+ final CompletableFuture<?> asyncUpdate = CompletableFuture.runAsync(
+ () -> domainObject.setMyEnum(valueToSet))
+ .exceptionally(x -> {
+ thrown.add(x);
+ return null;
+ });
+
+ do {
+ SWTTestUtil.waitForUIThread();
+ } while (!asyncUpdate.isDone());
+ }
+
+ assertThat("Async update failed", thrown, not(hasItem(anything())));
+
+ // Got notified once for each async update
+ verify(listener, times(valuesToSet.length)).handleChange(any());
+ } catch (DatabindingFailedException | NoRendererFoundException | NoPropertyDescriptorFoundExeption e) {
+ sneakyThrow(e);
+ }
+ });
+ }
+
+ private static <X extends Exception> void sneakyThrow(Exception x) throws X {
+ throw (X) x;
+ }
+
}