diff --git a/gipsl.all.build.mappingpreservation/.classpath b/gipsl.all.build.mappingpreservation/.classpath
new file mode 100644
index 00000000..0b423a2c
--- /dev/null
+++ b/gipsl.all.build.mappingpreservation/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/gipsl.all.build.mappingpreservation/.project b/gipsl.all.build.mappingpreservation/.project
new file mode 100644
index 00000000..15c5fda9
--- /dev/null
+++ b/gipsl.all.build.mappingpreservation/.project
@@ -0,0 +1,25 @@
+
+
+ gipsl.all.build.mappingpreservation
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.xtext.ui.shared.xtextBuilder
+
+
+
+
+
+ org.emoflon.gips.gipsl.ui.gipsNature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.pde.PluginNature
+ org.eclipse.xtext.ui.shared.xtextNature
+
+
diff --git a/gipsl.all.build.mappingpreservation/.settings/org.eclipse.jdt.core.prefs b/gipsl.all.build.mappingpreservation/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..3a79233b
--- /dev/null
+++ b/gipsl.all.build.mappingpreservation/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,10 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
+org.eclipse.jdt.core.compiler.compliance=21
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=21
diff --git a/gipsl.all.build.mappingpreservation/META-INF/MANIFEST.MF b/gipsl.all.build.mappingpreservation/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..3d204907
--- /dev/null
+++ b/gipsl.all.build.mappingpreservation/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Automatic-Module-Name: gipsl.all.build.mappingpreservation
+Bundle-ManifestVersion: 2
+Bundle-Name: gipsl.all.build.mappingpreservation
+Bundle-Vendor: My Company
+Bundle-Version: 1.0.0.qualifier
+Export-Package: gipsl.all.build.mappingpreservation.connector
+Bundle-SymbolicName: gipsl.all.build.mappingpreservation; singleton:=true
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-21
+Require-Bundle: org.emoflon.ibex.common,
+ org.emoflon.ibex.gt,
+ org.emoflon.gips.core,
+ org.emoflon.ibex.gt.democles,
+ org.emoflon.ibex.gt.hipe,
+ gipsl.all.build.model;bundle-version="0.0.1",
+ test.suite.utils;bundle-version="1.0.0"
diff --git a/gipsl.all.build.mappingpreservation/build.properties b/gipsl.all.build.mappingpreservation/build.properties
new file mode 100644
index 00000000..14acfb7c
--- /dev/null
+++ b/gipsl.all.build.mappingpreservation/build.properties
@@ -0,0 +1,5 @@
+source.. = src/,\
+ src-gen/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/gipsl.all.build.mappingpreservation/src/gipsl/all/build/mappingpreservation/Model.gipsl b/gipsl.all.build.mappingpreservation/src/gipsl/all/build/mappingpreservation/Model.gipsl
new file mode 100644
index 00000000..e1db1241
--- /dev/null
+++ b/gipsl.all.build.mappingpreservation/src/gipsl/all/build/mappingpreservation/Model.gipsl
@@ -0,0 +1,100 @@
+package "gipsl.all.build.mappingpreservation"
+import "platform:/resource/gipsl.all.build.model/model/Model.ecore"
+
+config {
+ solver := GUROBI [home:="fu", license:="bar"];
+ launchConfig := true [main := "TODO"];
+ timeLimit := true [value := 5.0];
+ randomSeed := true [value := 0];
+ presolve := true;
+ debugOutput := false;
+}
+
+condition vnodeNotMapped = forbid vnodeIsMapped
+pattern vnodeIsMapped {
+ host: SubstrateNode
+
+ vnode: VirtualNode {
+ -host -> host
+ }
+}
+
+pattern vnodeNotMapped {
+ vnode: VirtualNode
+}
+when vnodeNotMapped
+
+rule mapVnode {
+ root: Root {
+ -containers -> substrateContainer
+ -containers -> virtualContainer
+ }
+
+ substrateContainer: SubstrateContainer {
+ -substrateNodes -> snode
+ }
+
+ virtualContainer: VirtualContainer {
+ -virtualNodes -> vnode
+ }
+
+ snode: SubstrateResourceNode
+
+ vnode: VirtualResourceNode {
+ ++ -host -> snode
+ }
+
+ # vnode.resourceDemand != 10
+}
+
+rule mapVnodeWith10ResDem {
+ root: Root {
+ -containers -> substrateContainer
+ -containers -> virtualContainer
+ }
+
+ substrateContainer: SubstrateContainer {
+ -substrateNodes -> snode
+ }
+
+ virtualContainer: VirtualContainer {
+ -virtualNodes -> vnode
+ }
+
+ snode: SubstrateResourceNode
+
+ vnode: VirtualResourceNode {
+ ++ -host -> snode
+ }
+
+ # vnode.resourceDemand == 10
+}
+
+//
+// GIPSL starts here!
+//
+
+mapping n2n with mapVnode;
+mapping resDem with mapVnodeWith10ResDem;
+
+// At most one mapping per virtual node is allowed
+constraint -> class::VirtualNode {
+ mappings.n2n->filter(m | m.nodes().vnode == self)->count() <= 1
+}
+
+constraint -> class::VirtualNode {
+ mappings.resDem->filter(m | m.nodes().vnode == self)->count() <= 1
+}
+
+// Cost = 1 per mapped virtual node
+objective maps -> mapping::n2n {
+ 1
+}
+
+objective map10ResDem -> mapping::resDem {
+ 1
+}
+
+global objective : max {
+ maps + map10ResDem
+}
diff --git a/gipsl.all.build.mappingpreservation/src/gipsl/all/build/mappingpreservation/connector/MappingPreservationConnector.java b/gipsl.all.build.mappingpreservation/src/gipsl/all/build/mappingpreservation/connector/MappingPreservationConnector.java
new file mode 100644
index 00000000..a42f995b
--- /dev/null
+++ b/gipsl.all.build.mappingpreservation/src/gipsl/all/build/mappingpreservation/connector/MappingPreservationConnector.java
@@ -0,0 +1,93 @@
+package gipsl.all.build.mappingpreservation.connector;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.emoflon.gips.core.ilp.ILPSolverOutput;
+
+import gipsl.all.build.mappingpreservation.api.gips.MappingpreservationGipsAPI;
+import gipsl.all.build.mappingpreservation.api.gips.mapping.N2nMapping;
+import gipsl.all.build.mappingpreservation.api.gips.mapping.ResDemMapping;
+import gipsl.all.build.mappingpreservation.api.matches.MapVnodeMatch;
+import gipsl.all.build.mappingpreservation.api.matches.MapVnodeWith10ResDemMatch;
+import test.suite.gips.utils.AConnector;
+import test.suite.gips.utils.GipsTestUtils;
+import test.suite.gips.utils.GlobalTestConfig;
+
+public class MappingPreservationConnector extends AConnector {
+
+ public MappingPreservationConnector(final String modelPath) {
+ api = new MappingpreservationGipsAPI();
+ api.init(GipsTestUtils.pathToAbsUri(modelPath));
+ GlobalTestConfig.overrideSolver(api);
+ }
+
+ @Override
+ public ILPSolverOutput run(final String outputPath) {
+ final ILPSolverOutput output = solve();
+ ((MappingpreservationGipsAPI) api).getN2n().applyNonZeroMappings();
+ save(outputPath);
+ return output;
+ }
+
+ public ILPSolverOutput runWithNoApplication(final String outputPath) {
+ final ILPSolverOutput output = solve();
+ save(outputPath);
+ return output;
+ }
+
+ public List> applyMappingWithVnodeName(final String vnodeName) {
+ final var mappings = ((MappingpreservationGipsAPI) api).getN2n().getNonZeroVariableMappings();
+ final var filtered = mappings.stream().filter(t -> {
+ return t.getMatch().getVnode().getName().equals(vnodeName);
+ }).toList();
+
+ // Check that only one mapping should be applied
+ if (filtered.size() != 1) {
+ throw new UnsupportedOperationException();
+ }
+
+ // Check if mapping to be applied has a value > 0
+ if (!(filtered.get(0).getValue() > 0)) {
+ throw new InternalError();
+ }
+
+ final var rule = ((MappingpreservationGipsAPI) api).getN2n().getGTRule();
+ return filtered.stream().map(m -> rule.apply(m.getMatch(), true)).collect(Collectors.toList());
+ }
+
+ public void save(final String path) {
+ super.save(path);
+ }
+
+ public Collection getN2nMappings() {
+ return ((MappingpreservationGipsAPI) api).getN2n().getMappings().values();
+ }
+
+ public Collection getResDemMappings() {
+ return ((MappingpreservationGipsAPI) api).getResDem().getMappings().values();
+ }
+
+ public List> applyMappingWithVnode10Name(final String vnodeName) {
+ final var mappings = ((MappingpreservationGipsAPI) api).getResDem().getNonZeroVariableMappings();
+ final var filtered = mappings.stream().filter(t -> {
+ return t.getMatch().getVnode().getName().equals(vnodeName);
+ }).toList();
+
+ // Check that only one mapping should be applied
+ if (filtered.size() != 1) {
+ throw new UnsupportedOperationException();
+ }
+
+ // Check if mapping to be applied has a value > 0
+ if (!(filtered.get(0).getValue() > 0)) {
+ throw new InternalError();
+ }
+
+ final var rule = ((MappingpreservationGipsAPI) api).getResDem().getGTRule();
+ return filtered.stream().map(m -> rule.apply(m.getMatch(), true)).collect(Collectors.toList());
+ }
+
+}
diff --git a/gipsl.all.build.mappingpreservationb/.classpath b/gipsl.all.build.mappingpreservationb/.classpath
new file mode 100644
index 00000000..0b423a2c
--- /dev/null
+++ b/gipsl.all.build.mappingpreservationb/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/gipsl.all.build.mappingpreservationb/.project b/gipsl.all.build.mappingpreservationb/.project
new file mode 100644
index 00000000..756a6069
--- /dev/null
+++ b/gipsl.all.build.mappingpreservationb/.project
@@ -0,0 +1,25 @@
+
+
+ gipsl.all.build.mappingpreservationb
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.xtext.ui.shared.xtextBuilder
+
+
+
+
+
+ org.emoflon.gips.gipsl.ui.gipsNature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.pde.PluginNature
+ org.eclipse.xtext.ui.shared.xtextNature
+
+
diff --git a/gipsl.all.build.mappingpreservationb/.settings/org.eclipse.jdt.core.prefs b/gipsl.all.build.mappingpreservationb/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..3a79233b
--- /dev/null
+++ b/gipsl.all.build.mappingpreservationb/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,10 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
+org.eclipse.jdt.core.compiler.compliance=21
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=21
diff --git a/gipsl.all.build.mappingpreservationb/META-INF/MANIFEST.MF b/gipsl.all.build.mappingpreservationb/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..07661c8e
--- /dev/null
+++ b/gipsl.all.build.mappingpreservationb/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Automatic-Module-Name: gipsl.all.build.mappingpreservationb
+Bundle-ManifestVersion: 2
+Bundle-Name: gipsl.all.build.mappingpreservationb
+Bundle-Vendor: My Company
+Bundle-Version: 1.0.0.qualifier
+Export-Package: gipsl.all.build.mappingpreservationb.connector
+Bundle-SymbolicName: gipsl.all.build.mappingpreservationb; singleton:=true
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-21
+Require-Bundle: org.emoflon.ibex.common,
+ org.emoflon.ibex.gt,
+ org.emoflon.gips.core,
+ org.emoflon.ibex.gt.democles,
+ org.emoflon.ibex.gt.hipe,
+ gipsl.all.build.model,
+ test.suite.utils
diff --git a/gipsl.all.build.mappingpreservationb/build.properties b/gipsl.all.build.mappingpreservationb/build.properties
new file mode 100644
index 00000000..14acfb7c
--- /dev/null
+++ b/gipsl.all.build.mappingpreservationb/build.properties
@@ -0,0 +1,5 @@
+source.. = src/,\
+ src-gen/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/gipsl.all.build.mappingpreservationb/src/gipsl/all/build/mappingpreservationb/Model.gipsl b/gipsl.all.build.mappingpreservationb/src/gipsl/all/build/mappingpreservationb/Model.gipsl
new file mode 100644
index 00000000..96797168
--- /dev/null
+++ b/gipsl.all.build.mappingpreservationb/src/gipsl/all/build/mappingpreservationb/Model.gipsl
@@ -0,0 +1,66 @@
+package "gipsl.all.build.mappingpreservationb"
+import "platform:/resource/gipsl.all.build.model/model/Model.ecore"
+
+config {
+ solver := GUROBI [home:="fu", license:="bar"];
+ launchConfig := true [main := "TODO"];
+ timeLimit := true [value := 5.0];
+ randomSeed := true [value := 0];
+ presolve := true;
+ debugOutput := false;
+}
+
+rule incrementVNodeRD {
+ root: Root {
+ -containers -> virtualContainer
+ }
+
+ virtualContainer: VirtualContainer {
+ -virtualNodes -> vnode
+ }
+
+ vnode: VirtualResourceNode {
+ .resourceDemand := vnode.resourceDemand + 1
+ }
+
+// # vnode.resourceDemand != 10
+}
+
+rule incrementVNodeRDIfZero {
+ root: Root {
+ -containers -> virtualContainer
+ }
+
+ virtualContainer: VirtualContainer {
+ -virtualNodes -> vnode
+ }
+
+ vnode: VirtualResourceNode {
+ .resourceDemand := vnode.resourceDemand + 1
+ }
+
+ # vnode.resourceDemand == 0
+}
+
+mapping incr with incrementVNodeRD;
+mapping incrIfZero with incrementVNodeRDIfZero;
+
+constraint -> class::VirtualNode {
+ mappings.incr->filter(m | m.nodes().vnode == self)->count() == 1
+}
+
+constraint -> pattern::incrementVNodeRDIfZero {
+ mappings.incrIfZero->filter(m | m.nodes().vnode == self.nodes().vnode)->count() == 1
+}
+
+objective objIncr -> mapping::incr {
+ 1
+}
+
+objective objIncrIfZero -> mapping::incrIfZero {
+ 1
+}
+
+global objective : max {
+ objIncr + objIncrIfZero
+}
\ No newline at end of file
diff --git a/gipsl.all.build.mappingpreservationb/src/gipsl/all/build/mappingpreservationb/connector/MappingPreservationBConnector.java b/gipsl.all.build.mappingpreservationb/src/gipsl/all/build/mappingpreservationb/connector/MappingPreservationBConnector.java
new file mode 100644
index 00000000..d8e2c808
--- /dev/null
+++ b/gipsl.all.build.mappingpreservationb/src/gipsl/all/build/mappingpreservationb/connector/MappingPreservationBConnector.java
@@ -0,0 +1,75 @@
+package gipsl.all.build.mappingpreservationb.connector;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.emoflon.gips.core.ilp.ILPSolverOutput;
+
+import gipsl.all.build.mappingpreservationb.api.gips.MappingpreservationbGipsAPI;
+import gipsl.all.build.mappingpreservationb.api.gips.mapping.IncrIfZeroMapping;
+import gipsl.all.build.mappingpreservationb.api.gips.mapping.IncrMapping;
+import gipsl.all.build.mappingpreservationb.api.matches.IncrementVNodeRDMatch;
+import test.suite.gips.utils.AConnector;
+import test.suite.gips.utils.GipsTestUtils;
+import test.suite.gips.utils.GlobalTestConfig;
+
+public class MappingPreservationBConnector extends AConnector {
+
+ public MappingPreservationBConnector(final String modelPath) {
+ api = new MappingpreservationbGipsAPI();
+ api.init(GipsTestUtils.pathToAbsUri(modelPath));
+ GlobalTestConfig.overrideSolver(api);
+ }
+
+ @Override
+ public ILPSolverOutput run(final String outputPath) {
+ final ILPSolverOutput output = solve();
+ save(outputPath);
+ return output;
+ }
+
+ public ILPSolverOutput runWithNoApplication(final String outputPath) {
+ final ILPSolverOutput output = solve();
+ save(outputPath);
+ return output;
+ }
+
+ public void applyIncrMatches() {
+ ((MappingpreservationbGipsAPI) api).getIncr().applyNonZeroMappings();
+ }
+
+// public List> applyMappingWithVnodeName(final String vnodeName) {
+// final var mappings = ((MappingpreservationbGipsAPI) api).getIncr().getNonZeroVariableMappings();
+// final var filtered = mappings.stream().filter(t -> {
+// return t.getMatch().getVnode().getName().equals(vnodeName);
+// }).toList();
+//
+// // Check that only one mapping should be applied
+// if (filtered.size() != 1) {
+// throw new UnsupportedOperationException();
+// }
+//
+// // Check if mapping to be applied has a value > 0
+// if (!(filtered.get(0).getValue() > 0)) {
+// throw new InternalError();
+// }
+//
+// final var rule = ((MappingpreservationbGipsAPI) api).getIncr().getGTRule();
+// return filtered.stream().map(m -> rule.apply(m.getMatch(), true)).collect(Collectors.toList());
+// }
+
+ public void save(final String path) {
+ super.save(path);
+ }
+
+ public Collection getIncrMappings() {
+ return ((MappingpreservationbGipsAPI) api).getIncr().getMappings().values();
+ }
+
+ public Collection getIncrIfZeroMappings() {
+ return ((MappingpreservationbGipsAPI) api).getIncrIfZero().getMappings().values();
+ }
+
+}
diff --git a/test.suite.gips/META-INF/MANIFEST.MF b/test.suite.gips/META-INF/MANIFEST.MF
index f03cafec..3b284cef 100644
--- a/test.suite.gips/META-INF/MANIFEST.MF
+++ b/test.suite.gips/META-INF/MANIFEST.MF
@@ -40,6 +40,8 @@ Import-Package: gips.enumequals.connector,
gipsl.all.build.implication.connector,
gipsl.all.build.implicationstatic.connector,
gipsl.all.build.inheritedtypecontext.connector,
+ gipsl.all.build.mappingpreservation.connector,
+ gipsl.all.build.mappingpreservationb.connector,
gipsl.all.build.nogt.connector,
gipsl.all.build.objbug.connector,
gipsl.all.build.objbugb.connector,
diff --git a/test.suite.gips/src/test/suite/gipsl/all/build/GipslAllBuildMappingPreservationBTest.java b/test.suite.gips/src/test/suite/gipsl/all/build/GipslAllBuildMappingPreservationBTest.java
new file mode 100644
index 00000000..cd90eb6c
--- /dev/null
+++ b/test.suite.gips/src/test/suite/gipsl/all/build/GipslAllBuildMappingPreservationBTest.java
@@ -0,0 +1,88 @@
+package test.suite.gipsl.all.build;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.File;
+
+import org.emoflon.gips.core.ilp.ILPSolverOutput;
+import org.emoflon.gips.core.ilp.ILPSolverStatus;
+import org.junit.jupiter.api.Test;
+
+import gipsl.all.build.mappingpreservationb.connector.MappingPreservationBConnector;
+import model.Container;
+import model.Root;
+import model.VirtualContainer;
+import model.VirtualNode;
+import model.VirtualResourceNode;
+
+public class GipslAllBuildMappingPreservationBTest extends AGipslAllBuildTest {
+
+ // Setup method
+
+ public void callableSetUp() {
+ gen.persistModel(MODEL_PATH);
+ con = new MappingPreservationBConnector(MODEL_PATH);
+
+ // Delete possible file of previous run
+ final File output = new File(OUTPUT_PATH);
+ output.delete();
+ }
+
+ // Actual tests
+ // Positive tests
+
+ @Test
+ public void testOneVNodeZero() {
+ gen.genVirtualNode("v1", 0);
+ callableSetUp();
+
+ final ILPSolverOutput ret = ((MappingPreservationBConnector) con).runWithNoApplication(OUTPUT_PATH);
+
+ // Pre-checks
+ assertEquals(ILPSolverStatus.OPTIMAL, ret.status());
+
+ // One VNode with two applied mappings
+ assertEquals(2, Math.abs(ret.objectiveValue()));
+
+ gen.loadModel(OUTPUT_PATH);
+
+ // Check model state (pre-first application)
+ checkNumberOfZeroVnodes(1);
+ assertEquals(1, ((MappingPreservationBConnector) con).getIncrMappings().size());
+ assertEquals(1, ((MappingPreservationBConnector) con).getIncrIfZeroMappings().size());
+
+ // Apply all (one) mapping of type "increment"
+ ((MappingPreservationBConnector) con).applyIncrMatches();
+
+ // First mapping (of type "increment") must be present
+ assertEquals(1, ((MappingPreservationBConnector) con).getIncrMappings().size());
+
+ // Second mapping (of type "increment if zero") will be gone
+ assertEquals(0, ((MappingPreservationBConnector) con).getIncrIfZeroMappings().size());
+ }
+
+ //
+ // Utility methods
+ //
+
+ private void checkNumberOfZeroVnodes(final int expected) {
+ int vnodeCntr = 0;
+
+ final Root root = gen.getRoot();
+ for (final Container c : root.getContainers()) {
+ if (c instanceof VirtualContainer) {
+ final VirtualContainer vc = (VirtualContainer) c;
+ for (final VirtualNode vn : vc.getVirtualNodes()) {
+ if (vn instanceof VirtualResourceNode vrn) {
+ if (vrn.getResourceDemand() == 0) {
+ vnodeCntr++;
+ }
+ }
+ }
+ }
+ }
+
+ assertEquals(expected, vnodeCntr);
+ }
+
+}
diff --git a/test.suite.gips/src/test/suite/gipsl/all/build/GipslAllBuildMappingPreservationTest.java b/test.suite.gips/src/test/suite/gipsl/all/build/GipslAllBuildMappingPreservationTest.java
new file mode 100644
index 00000000..9c4b9e5e
--- /dev/null
+++ b/test.suite.gips/src/test/suite/gipsl/all/build/GipslAllBuildMappingPreservationTest.java
@@ -0,0 +1,200 @@
+package test.suite.gipsl.all.build;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.emoflon.gips.core.ilp.ILPSolverOutput;
+import org.emoflon.gips.core.ilp.ILPSolverStatus;
+import org.junit.jupiter.api.Test;
+
+import gipsl.all.build.mappingpreservation.api.gips.mapping.N2nMapping;
+import gipsl.all.build.mappingpreservation.api.gips.mapping.ResDemMapping;
+import gipsl.all.build.mappingpreservation.connector.MappingPreservationConnector;
+import model.Container;
+import model.Root;
+import model.VirtualContainer;
+import model.VirtualNode;
+
+public class GipslAllBuildMappingPreservationTest extends AGipslAllBuildTest {
+
+ // Setup method
+
+ public void callableSetUp() {
+ gen.persistModel(MODEL_PATH);
+ con = new MappingPreservationConnector(MODEL_PATH);
+
+ // Delete possible file of previous run
+ final File output = new File(OUTPUT_PATH);
+ output.delete();
+ }
+
+ // Actual tests
+ // Positive tests
+
+ @Test
+ public void testMap2to1IndividualApplication() {
+ gen.genSubstrateNode("s1", 2);
+ gen.genVirtualNode("v1", 1);
+ gen.genVirtualNode("v2", 1);
+ callableSetUp();
+
+ final ILPSolverOutput ret = ((MappingPreservationConnector) con).runWithNoApplication(OUTPUT_PATH);
+
+ // Pre-checks
+ assertEquals(ILPSolverStatus.OPTIMAL, ret.status());
+ assertEquals(2, Math.abs(ret.objectiveValue()));
+
+ gen.loadModel(OUTPUT_PATH);
+
+ // Check model state (pre-first application)
+ checkNumberOfEmbeddedVnodes(0);
+
+ final var appliedFirst = ((MappingPreservationConnector) con).applyMappingWithVnodeName("v1");
+ assertEquals(1, appliedFirst.size());
+ assertEquals("v1", appliedFirst.get(0).get().getVnode().getName());
+
+ // Check model state (after first application)
+ ((MappingPreservationConnector) con).save(OUTPUT_PATH);
+ gen.loadModel(OUTPUT_PATH);
+ checkNumberOfEmbeddedVnodes(1);
+
+ final var appliedSecond = ((MappingPreservationConnector) con).applyMappingWithVnodeName("v2");
+ assertEquals(1, appliedSecond.size());
+ assertEquals("v2", appliedSecond.get(0).get().getVnode().getName());
+
+ // Check model state (after second application)
+ ((MappingPreservationConnector) con).save(OUTPUT_PATH);
+ gen.loadModel(OUTPUT_PATH);
+ checkNumberOfEmbeddedVnodes(2);
+ }
+
+ @Test
+ public void testMap2to1ApplyNonZeroMappings() {
+ gen.genSubstrateNode("s1", 2);
+ gen.genVirtualNode("v1", 1);
+ gen.genVirtualNode("v2", 1);
+ callableSetUp();
+
+ final ILPSolverOutput ret = ((MappingPreservationConnector) con).run(OUTPUT_PATH);
+ assertEquals(ILPSolverStatus.OPTIMAL, ret.status());
+ assertEquals(2, Math.abs(ret.objectiveValue()));
+
+ // Check model state
+ gen.loadModel(OUTPUT_PATH);
+ checkNumberOfEmbeddedVnodes(2);
+
+ // Actual check: Both mappings must still have the value > 0, even if PM got
+ // updated
+ final Collection mappings = ((MappingPreservationConnector) con).getN2nMappings();
+ // There are only two possible combinations
+ assertEquals(2, mappings.size());
+ for (final N2nMapping m : mappings) {
+ assertTrue(m.getValue() > 0);
+ }
+ }
+
+ @Test
+ public void testMap10to3ApplyNonZeroMappings() {
+ gen.genSubstrateNode("s1", 4);
+ gen.genSubstrateNode("s2", 4);
+ gen.genSubstrateNode("s3", 4);
+ for (int i = 1; i <= 10; i++) {
+ gen.genVirtualNode("v" + i, 1);
+ }
+ callableSetUp();
+
+ final ILPSolverOutput ret = ((MappingPreservationConnector) con).run(OUTPUT_PATH);
+ assertEquals(ILPSolverStatus.OPTIMAL, ret.status());
+ assertEquals(10, Math.abs(ret.objectiveValue()));
+
+ // Check model state
+ gen.loadModel(OUTPUT_PATH);
+ checkNumberOfEmbeddedVnodes(10);
+
+ // Actual check: Both mappings must still have the value > 0, even if PM got
+ // updated
+ final Collection mappings = ((MappingPreservationConnector) con).getN2nMappings();
+ // There are 30 possible combinations
+ assertEquals(30, mappings.size());
+ // Check that there are exactly 10 mappings with value > 0 (one for each virtual
+ // node)
+ int counter = 0;
+ for (final N2nMapping m : mappings) {
+ if (m.getValue() > 0) {
+ counter++;
+ }
+ }
+ assertEquals(10, counter);
+ }
+
+ @Test
+ public void testTwoSpecMappingsAfterEachOther() {
+ gen.genSubstrateNode("s1", 100);
+ gen.genVirtualNode("v1", 1);
+ gen.genVirtualNode("v2", 1);
+ gen.genVirtualNode("v100", 10);
+ callableSetUp();
+
+ // The first run application does not apply the matches for rule #2.
+ // (This is intended!)
+ final ILPSolverOutput ret = ((MappingPreservationConnector) con).run(OUTPUT_PATH);
+ assertEquals(ILPSolverStatus.OPTIMAL, ret.status());
+ assertEquals(3, Math.abs(ret.objectiveValue()));
+
+ // Check model state
+ gen.loadModel(OUTPUT_PATH);
+ checkNumberOfEmbeddedVnodes(2);
+
+ // Check that all valid matches of rule #2 are still selected (value > 0)
+ final Collection resDemMappings = ((MappingPreservationConnector) con).getResDemMappings();
+ // There is 1 possible combination
+ assertEquals(1, resDemMappings.size());
+ // The one mapping must also be selected
+ for (final ResDemMapping m : resDemMappings) {
+ assertTrue(m.getValue() > 0);
+ }
+
+ // Next step: Also apply the last mapping
+ final var appliedSecond = ((MappingPreservationConnector) con).applyMappingWithVnode10Name("v100");
+ assertEquals(1, appliedSecond.size());
+ assertEquals("v100", appliedSecond.get(0).get().getVnode().getName());
+
+ // Check model state (after second application)
+ ((MappingPreservationConnector) con).save(OUTPUT_PATH);
+ gen.loadModel(OUTPUT_PATH);
+ checkNumberOfEmbeddedVnodes(3);
+
+ // Check N2N mapping afterwards
+ final Collection n2nMappings = ((MappingPreservationConnector) con).getN2nMappings();
+ // There are 2 possible combinations
+ assertEquals(2, n2nMappings.size());
+ // All two mappings must also be selected
+ for (final N2nMapping m : n2nMappings) {
+ assertTrue(m.getValue() > 0);
+ }
+ }
+
+ // Utility methods
+
+ private void checkNumberOfEmbeddedVnodes(final int expected) {
+ int hostedVnodeCntr = 0;
+
+ final Root root = gen.getRoot();
+ for (final Container c : root.getContainers()) {
+ if (c instanceof VirtualContainer) {
+ final VirtualContainer vc = (VirtualContainer) c;
+ for (final VirtualNode vn : vc.getVirtualNodes()) {
+ if (vn.getHost() != null) {
+ hostedVnodeCntr++;
+ }
+ }
+ }
+ }
+
+ assertEquals(expected, hostedVnodeCntr);
+ }
+
+}
diff --git a/test.suite.gips/src/test/suite/gipsl/all/build/utils/AllBuildModelGenerator.java b/test.suite.gips/src/test/suite/gipsl/all/build/utils/AllBuildModelGenerator.java
index 116eb93b..b15435d9 100644
--- a/test.suite.gips/src/test/suite/gipsl/all/build/utils/AllBuildModelGenerator.java
+++ b/test.suite.gips/src/test/suite/gipsl/all/build/utils/AllBuildModelGenerator.java
@@ -86,4 +86,8 @@ public Container getContainer(final String name) {
return null;
}
+ public Root getRoot() {
+ return root;
+ }
+
}