[549069] Iterate the semantic elements with a fixed order

* When the semanticCandidatesExpression is not set, the whole semantic
model is iterated.
Now the result is returned in a deterministic order.
Moreover, the returned collection is not copied in an immutable
collection anymore.
* A test has been added.

Bug: 549069
Change-Id: Ib987de6edf675a90f617e24ed247a048d3872052
Signed-off-by: Laurent Fasani <laurent.fasani@obeo.fr>
diff --git a/plugins/org.eclipse.sirius.diagram/src-core/org/eclipse/sirius/diagram/business/internal/sync/AbstractSynchronizerHelper.java b/plugins/org.eclipse.sirius.diagram/src-core/org/eclipse/sirius/diagram/business/internal/sync/AbstractSynchronizerHelper.java
index d9020dc..e000f76 100644
--- a/plugins/org.eclipse.sirius.diagram/src-core/org/eclipse/sirius/diagram/business/internal/sync/AbstractSynchronizerHelper.java
+++ b/plugins/org.eclipse.sirius.diagram/src-core/org/eclipse/sirius/diagram/business/internal/sync/AbstractSynchronizerHelper.java
@@ -14,6 +14,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.LinkedHashSet;
 import java.util.stream.Collectors;
 
 import org.eclipse.emf.ecore.EObject;
@@ -145,10 +146,10 @@
      */
     protected Collection<EObject> getPreviousSemanticsElements(DragAndDropTarget container, DiagramElementMapping mapping) {
         // @formatter:off
-        return ImmutableSet.copyOf(sync.getPreviousDiagramElements(container, mapping).stream()
+        return sync.getPreviousDiagramElements(container, mapping).stream()
                 .map(DDiagramElement::getTarget)
                 .filter(input -> input != null && ((input.eContainer() != null && input.eContainer().eResource() != null) || input.eResource() != null))
-                .collect(Collectors.toSet()));
+                .collect(Collectors.toCollection(LinkedHashSet::new));
         // @formatter:on
     }
 
diff --git a/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/My.ecore b/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/My.ecore
new file mode 100644
index 0000000..bc4ae24
--- /dev/null
+++ b/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/My.ecore
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="">
+  <eClassifiers xsi:type="ecore:EClass" name="E1" eSuperTypes="#//E2"/>
+  <eClassifiers xsi:type="ecore:EClass" name="E2"/>
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/My.odesign b/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/My.odesign
new file mode 100644
index 0000000..c4d1686
--- /dev/null
+++ b/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/My.odesign
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<description:Group xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:description="http://www.eclipse.org/sirius/description/1.1.0" xmlns:description_1="http://www.eclipse.org/sirius/diagram/description/1.1.0" xmlns:style="http://www.eclipse.org/sirius/diagram/description/style/1.1.0" name="My" version="12.0.0.2017041100">
+  <ownedViewpoints name="SimpleVP" modelFileExtension="*.ecore">
+    <ownedRepresentations xsi:type="description_1:DiagramDescription" name="MyDiagram" domainClass="ecore::EPackage" enablePopupBars="true">
+      <metamodel href="http://www.eclipse.org/emf/2002/Ecore#/"/>
+      <defaultLayer name="Default">
+        <nodeMappings name="MD_EClassNodes" domainClass="ecore::EClass">
+          <style xsi:type="style:SquareDescription" labelSize="12" labelPosition="node" resizeKind="NSEW">
+            <borderColor xsi:type="description:SystemColor" href="environment:/viewpoint#//@systemColors/@entries[name='black']"/>
+            <labelColor xsi:type="description:SystemColor" href="environment:/viewpoint#//@systemColors/@entries[name='black']"/>
+            <color xsi:type="description:SystemColor" href="environment:/viewpoint#//@systemColors/@entries[name='gray']"/>
+          </style>
+        </nodeMappings>
+      </defaultLayer>
+    </ownedRepresentations>
+  </ownedViewpoints>
+</description:Group>
diff --git a/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/representations.aird b/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/representations.aird
new file mode 100644
index 0000000..0f537f6
--- /dev/null
+++ b/plugins/org.eclipse.sirius.tests.junit/data/unit/refresh/stability/representations.aird
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:description="http://www.eclipse.org/sirius/description/1.1.0" xmlns:description_1="http://www.eclipse.org/sirius/diagram/description/1.1.0" xmlns:diagram="http://www.eclipse.org/sirius/diagram/1.1.0" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.2/notation" xmlns:style="http://www.eclipse.org/sirius/diagram/description/style/1.1.0" xmlns:viewpoint="http://www.eclipse.org/sirius/1.1.0" xsi:schemaLocation="http://www.eclipse.org/sirius/description/1.1.0 http://www.eclipse.org/sirius/1.1.0#//description http://www.eclipse.org/sirius/diagram/description/1.1.0 http://www.eclipse.org/sirius/diagram/1.1.0#//description http://www.eclipse.org/sirius/diagram/description/style/1.1.0 http://www.eclipse.org/sirius/diagram/1.1.0#//description/style">
+  <viewpoint:DAnalysis uid="_CfBW8KGMEemcAP0suDVxhA" selectedViews="_DdDFcKGMEemcAP0suDVxhA" version="14.1.0.201810161215">
+    <semanticResources>My.ecore</semanticResources>
+    <ownedViews xmi:type="viewpoint:DView" uid="_DdDFcKGMEemcAP0suDVxhA">
+      <viewpoint xmi:type="description:Viewpoint" href="My.odesign#//@ownedViewpoints[name='SimpleVP']"/>
+      <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_ExLm0KGMEemcAP0suDVxhA" name="new MyDiagram" repPath="#_EvuOQKGMEemcAP0suDVxhA">
+        <description xmi:type="description_1:DiagramDescription" href="My.odesign#//@ownedViewpoints[name='SimpleVP']/@ownedRepresentations[name='MyDiagram']"/>
+        <target xmi:type="ecore:EPackage" href="My.ecore#/"/>
+      </ownedRepresentationDescriptors>
+    </ownedViews>
+  </viewpoint:DAnalysis>
+  <diagram:DSemanticDiagram uid="_EvuOQKGMEemcAP0suDVxhA" name="new MyDiagram">
+    <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_ExESEKGMEemcAP0suDVxhA" source="DANNOTATION_CUSTOMIZATION_KEY">
+      <data xmi:type="diagram:ComputedStyleDescriptionRegistry" uid="_ExE5IKGMEemcAP0suDVxhA"/>
+    </ownedAnnotationEntries>
+    <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_Exo50KGMEemcAP0suDVxhA" source="GMF_DIAGRAMS">
+      <data xmi:type="notation:Diagram" xmi:id="_Exo50aGMEemcAP0suDVxhA" type="Sirius" element="_EvuOQKGMEemcAP0suDVxhA" measurementUnit="Pixel">
+        <children xmi:type="notation:Node" xmi:id="_Ex-4EKGMEemcAP0suDVxhA" type="2001" element="_EwseoKGMEemcAP0suDVxhA">
+          <children xmi:type="notation:Node" xmi:id="_EyKeQKGMEemcAP0suDVxhA" type="5002">
+            <layoutConstraint xmi:type="notation:Location" xmi:id="_EyKeQaGMEemcAP0suDVxhA" y="5"/>
+          </children>
+          <children xmi:type="notation:Node" xmi:id="_EycLEKGMEemcAP0suDVxhA" type="3003" element="_Ew0acKGMEemcAP0suDVxhA">
+            <styles xmi:type="notation:ShapeStyle" xmi:id="_EycLEaGMEemcAP0suDVxhA" fontName="Segoe UI"/>
+            <layoutConstraint xmi:type="notation:Bounds" xmi:id="_EycLEqGMEemcAP0suDVxhA"/>
+          </children>
+          <styles xmi:type="notation:ShapeStyle" xmi:id="_Ex-4EaGMEemcAP0suDVxhA" fontName="Segoe UI" fontHeight="12"/>
+          <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Ex-4EqGMEemcAP0suDVxhA" width="30" height="30"/>
+        </children>
+        <children xmi:type="notation:Node" xmi:id="_EyZHwKGMEemcAP0suDVxhA" type="2001" element="_ExAnsKGMEemcAP0suDVxhA">
+          <children xmi:type="notation:Node" xmi:id="_EyaV4KGMEemcAP0suDVxhA" type="5002">
+            <layoutConstraint xmi:type="notation:Location" xmi:id="_EyaV4aGMEemcAP0suDVxhA" y="5"/>
+          </children>
+          <children xmi:type="notation:Node" xmi:id="_EycyIKGMEemcAP0suDVxhA" type="3003" element="_ExAnsaGMEemcAP0suDVxhA">
+            <styles xmi:type="notation:ShapeStyle" xmi:id="_EycyIaGMEemcAP0suDVxhA" fontName="Segoe UI"/>
+            <layoutConstraint xmi:type="notation:Bounds" xmi:id="_EycyIqGMEemcAP0suDVxhA"/>
+          </children>
+          <styles xmi:type="notation:ShapeStyle" xmi:id="_EyZHwaGMEemcAP0suDVxhA" fontName="Segoe UI" fontHeight="12"/>
+          <layoutConstraint xmi:type="notation:Bounds" xmi:id="_EyZu0KGMEemcAP0suDVxhA" x="90" width="30" height="30"/>
+        </children>
+        <styles xmi:type="notation:DiagramStyle" xmi:id="_Exo50qGMEemcAP0suDVxhA"/>
+      </data>
+    </ownedAnnotationEntries>
+    <ownedDiagramElements xmi:type="diagram:DNode" uid="_EwseoKGMEemcAP0suDVxhA" name="E1" width="3" height="3" resizeKind="NSEW">
+      <target xmi:type="ecore:EClass" href="My.ecore#//E1"/>
+      <semanticElements xmi:type="ecore:EClass" href="My.ecore#//E1"/>
+      <ownedStyle xmi:type="diagram:Square" uid="_Ew0acKGMEemcAP0suDVxhA" labelSize="12" labelPosition="node">
+        <description xmi:type="style:SquareDescription" href="My.odesign#//@ownedViewpoints[name='SimpleVP']/@ownedRepresentations[name='MyDiagram']/@defaultLayer/@nodeMappings[name='MD_EClassNodes']/@style"/>
+      </ownedStyle>
+      <actualMapping xmi:type="description_1:NodeMapping" href="My.odesign#//@ownedViewpoints[name='SimpleVP']/@ownedRepresentations[name='MyDiagram']/@defaultLayer/@nodeMappings[name='MD_EClassNodes']"/>
+    </ownedDiagramElements>
+    <ownedDiagramElements xmi:type="diagram:DNode" uid="_ExAnsKGMEemcAP0suDVxhA" name="E2" width="3" height="3" resizeKind="NSEW">
+      <target xmi:type="ecore:EClass" href="My.ecore#//E2"/>
+      <semanticElements xmi:type="ecore:EClass" href="My.ecore#//E2"/>
+      <ownedStyle xmi:type="diagram:Square" uid="_ExAnsaGMEemcAP0suDVxhA" labelSize="12" labelPosition="node">
+        <description xmi:type="style:SquareDescription" href="My.odesign#//@ownedViewpoints[name='SimpleVP']/@ownedRepresentations[name='MyDiagram']/@defaultLayer/@nodeMappings[name='MD_EClassNodes']/@style"/>
+      </ownedStyle>
+      <actualMapping xmi:type="description_1:NodeMapping" href="My.odesign#//@ownedViewpoints[name='SimpleVP']/@ownedRepresentations[name='MyDiagram']/@defaultLayer/@nodeMappings[name='MD_EClassNodes']"/>
+    </ownedDiagramElements>
+    <description xmi:type="description_1:DiagramDescription" href="My.odesign#//@ownedViewpoints[name='SimpleVP']/@ownedRepresentations[name='MyDiagram']"/>
+    <filterVariableHistory xmi:type="diagram:FilterVariableHistory" uid="_EwAiIKGMEemcAP0suDVxhA"/>
+    <activatedLayers xmi:type="description_1:Layer" href="My.odesign#//@ownedViewpoints[name='SimpleVP']/@ownedRepresentations[name='MyDiagram']/@defaultLayer"/>
+    <target xmi:type="ecore:EPackage" href="My.ecore#/"/>
+  </diagram:DSemanticDiagram>
+</xmi:XMI>
diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/diagram/AllDiagramPluginsTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/diagram/AllDiagramPluginsTests.java
index eb99577..fd29a6e 100644
--- a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/diagram/AllDiagramPluginsTests.java
+++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/diagram/AllDiagramPluginsTests.java
@@ -45,6 +45,7 @@
 import org.eclipse.sirius.tests.unit.api.refresh.RefreshInUIThreadTests;
 import org.eclipse.sirius.tests.unit.api.refresh.RefreshOnDeletionInAutoRefreshTests;
 import org.eclipse.sirius.tests.unit.api.refresh.RefreshOnDeletionInManualRefreshTests;
+import org.eclipse.sirius.tests.unit.api.refresh.RefreshStabilityTests;
 import org.eclipse.sirius.tests.unit.api.refresh.RefreshWithCustomizedStyleTests;
 import org.eclipse.sirius.tests.unit.api.refresh.StyleCustomizationAndConditionalStyleDescriptionTest;
 import org.eclipse.sirius.tests.unit.api.refresh.StyleRefreshTests;
@@ -406,6 +407,7 @@
         suite.addTestSuite(StyleSizeChangeRefreshTest.class);
         suite.addTestSuite(ConditionalStyleRefreshTest.class);
         suite.addTestSuite(RefreshWithCustomizationTests.class);
+        suite.addTestSuite(RefreshStabilityTests.class);
 
         suite.addTestSuite(SessionTest.class);
         suite.addTestSuite(SessionServiceGMFDiagramTest.class);
@@ -551,8 +553,7 @@
     }
 
     /**
-     * Add the tests which for one reason or another are not part of the suite
-     * launched on each Gerrit verification.
+     * Add the tests which for one reason or another are not part of the suite launched on each Gerrit verification.
      * 
      * @param suite
      *            the suite to add the tests into.
diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/api/refresh/RefreshStabilityTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/api/refresh/RefreshStabilityTests.java
new file mode 100644
index 0000000..2bfef52
--- /dev/null
+++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/api/refresh/RefreshStabilityTests.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2019 THALES GLOBAL SERVICES.
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.sirius.tests.unit.api.refresh;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.sirius.business.api.session.SessionStatus;
+import org.eclipse.sirius.business.api.session.factory.SessionFactory;
+import org.eclipse.sirius.tests.SiriusTestsPlugin;
+import org.eclipse.sirius.tests.support.api.SiriusDiagramTestCase;
+import org.eclipse.sirius.tests.support.api.TestsUtil;
+import org.eclipse.sirius.ui.business.api.dialect.DialectUIManager;
+import org.eclipse.sirius.viewpoint.DRepresentation;
+import org.eclipse.ui.IEditorPart;
+
+/**
+ * Check that the diagram refresh algorithm produces a deterministic result.
+ * 
+ * @author <a href="mailto:laurent.fasani@obeo.fr">Laurent Fasani</a>
+ */
+public class RefreshStabilityTests extends SiriusDiagramTestCase {
+
+    private static final String PATH = "/data/unit/refresh/stability/";
+
+    private static final String SEMANTIC_MODEL_FILENAME = "My.ecore";
+
+    private static final String MODELER_FILENAME = "My.odesign";
+
+    private static final String AIRD_MODEL_FILENAME = "representations.aird";
+
+    /**
+     * Test that the refresh algorithm will process the semantic elements in a deterministic order when the semantic
+     * candidate expression is empty.
+     * 
+     * @throws Exception
+     */
+    public void testEmptySemanticCandidatesExpression() throws Exception {
+        copyFilesToTestProject(SiriusTestsPlugin.PLUGIN_ID, PATH, AIRD_MODEL_FILENAME, SEMANTIC_MODEL_FILENAME, MODELER_FILENAME);
+        URI sessionResourceURI = URI.createPlatformResourceURI("/" + TEMPORARY_PROJECT_NAME + "/" + AIRD_MODEL_FILENAME, true);
+
+        session = SessionFactory.INSTANCE.createSession(sessionResourceURI, new NullProgressMonitor());
+        // If there was a bug it could be random because of the usage of Set instead of List. So we launch it 10 times.
+        for (int i = 0; i < 10; i++) {
+            doTestEmptySemanticCandidatesExpression();
+        }
+    }
+
+    private void doTestEmptySemanticCandidatesExpression() {
+        session.open(new NullProgressMonitor());
+        TestsUtil.synchronizationWithUIThread();
+
+        DRepresentation dRepresentation = getRepresentationsByName("new MyDiagram").get(0);
+        IEditorPart editor = DialectUIManager.INSTANCE.openEditor(session, dRepresentation, new NullProgressMonitor());
+
+        assertEquals("Bad session status", SessionStatus.SYNC, session.getStatus());
+
+        DialectUIManager.INSTANCE.closeEditor(editor, false);
+        TestsUtil.synchronizationWithUIThread();
+
+        session.close(new NullProgressMonitor());
+        TestsUtil.synchronizationWithUIThread();
+    }
+
+}