From 36052b633897d5ae66e6b9dc39a367e5df00c95e Mon Sep 17 00:00:00 2001 From: Thomas Degueule Date: Thu, 27 Aug 2015 20:46:59 +0200 Subject: [PATCH] ESuperTypes tests + improved error tracing --- .../diverse/melange/lib/EcoreMerger.xtend | 45 +- .../melange/tests/EcoreMergerTest.xtend | 36 +- .../metamodels/merge/InvalidSuperTypes.ecore | 8 + .../metamodels/merge/SuperTypes.ecore | 11 + .../merge/expected/SuperTypes.ecore | 531 ++++++++++++++++++ 5 files changed, 605 insertions(+), 26 deletions(-) create mode 100644 tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/InvalidSuperTypes.ecore create mode 100644 tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/SuperTypes.ecore create mode 100644 tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/expected/SuperTypes.ecore diff --git a/plugins/fr.inria.diverse.melange.lib/src/main/java/fr/inria/diverse/melange/lib/EcoreMerger.xtend b/plugins/fr.inria.diverse.melange.lib/src/main/java/fr/inria/diverse/melange/lib/EcoreMerger.xtend index bd8d26b49..90c5c61a8 100644 --- a/plugins/fr.inria.diverse.melange.lib/src/main/java/fr/inria/diverse/melange/lib/EcoreMerger.xtend +++ b/plugins/fr.inria.diverse.melange.lib/src/main/java/fr/inria/diverse/melange/lib/EcoreMerger.xtend @@ -16,10 +16,12 @@ import org.eclipse.emf.ecore.EOperation import org.eclipse.emf.ecore.EPackage import org.eclipse.emf.ecore.EParameter import org.eclipse.emf.ecore.EReference +import org.eclipse.emf.ecore.EStructuralFeature import org.eclipse.emf.ecore.EcoreFactory import org.eclipse.emf.ecore.util.Diagnostician import org.eclipse.emf.ecore.util.EcoreUtil import org.eclipse.xtend.lib.annotations.Data +import org.eclipse.emf.ecore.EcorePackage @ImplementedBy(PackageMergeMerger) interface EcoreMerger { @@ -34,6 +36,7 @@ interface EcoreMerger { static class Conflict { EObject receiving EObject merged + EStructuralFeature feature String message Diagnostic emfDiagnostic } @@ -66,11 +69,11 @@ class PackageMergeMerger implements EcoreMerger { if (receiving === null || merged === null) return null if (receiving == merged || receiving.nsURI == merged.nsURI) - addConflict(receiving, merged, "Cannot merge packages with same URI") + addConflict(receiving, merged, null, "Cannot merge packages with same URI") if (receiving.allSubPkgs.contains(merged)) - addConflict(receiving, merged, "Receiving package cannot contain merged package") + addConflict(receiving, merged, null, "Receiving package cannot contain merged package") if (merged.allSubPkgs.contains(receiving)) - addConflict(receiving, merged, "Merged package cannot contain receiving package") + addConflict(receiving, merged, null, "Merged package cannot contain receiving package") // FIXME: We should check for forbidden cross-refs between receiving/merged // but this is quite costly @@ -88,7 +91,9 @@ class PackageMergeMerger implements EcoreMerger { val diagSource = d.data.head if (diagSource instanceof ENamedElement) { diagSource.EAnnotations.filter[source == ORIGIN_ANNOTATION_SOURCE].forEach[ann | - addConflict(diagSource, ann.references.head as ENamedElement, d) + val receivingElement = ann.references.head as ENamedElement + val feature = ann.references.get(1) as EStructuralFeature + addConflict(diagSource, receivingElement, feature, d) ] } ] @@ -202,16 +207,16 @@ class PackageMergeMerger implements EcoreMerger { private def dispatch void doMerge(EPackage rcv, EPackage merged) { // TODO: what about URIs, prefix, etc. ? - doCollectionsMerge(rcv, rcv.EClassifiers, merged.EClassifiers) - doCollectionsMerge(rcv, rcv.ESubpackages, merged.ESubpackages) + doCollectionsMerge(rcv, EcorePackage.Literals.EPACKAGE__ECLASSIFIERS, rcv.EClassifiers, merged.EClassifiers) + doCollectionsMerge(rcv, EcorePackage.Literals.EPACKAGE__ESUBPACKAGES, rcv.ESubpackages, merged.ESubpackages) } private def dispatch void doMerge(EClass rcv, EClass merged) { - // TODO: What about superTypes? rcv.abstract = rcv.abstract && merged.abstract rcv.interface = rcv.interface && merged.interface - doCollectionsMerge(rcv, rcv.EStructuralFeatures, merged.EStructuralFeatures) - doCollectionsMerge(rcv, rcv.EOperations, merged.EOperations) + doCollectionsMerge(rcv, EcorePackage.Literals.ECLASS__ESTRUCTURAL_FEATURES, rcv.EStructuralFeatures, merged.EStructuralFeatures) + doCollectionsMerge(rcv, EcorePackage.Literals.ECLASS__EOPERATIONS, rcv.EOperations, merged.EOperations) + doCollectionsMerge(rcv, EcorePackage.Literals.ECLASS__ESUPER_TYPES, rcv.ESuperTypes, merged.ESuperTypes) } private def dispatch void doMerge(EDataType rcv, EDataType merged) { @@ -244,7 +249,7 @@ class PackageMergeMerger implements EcoreMerger { rcv.upperBound = maxBound(rcv.upperBound, merged.upperBound) if (rcv.containment != merged.containment) - addConflict(rcv, merged, "Conflicting containment values for reference") + addConflict(rcv, merged, null, "Conflicting containment values for reference") } private def dispatch void doMerge(EOperation rcv, EOperation merged) { @@ -260,22 +265,23 @@ class PackageMergeMerger implements EcoreMerger { private def dispatch void doMerge(EAnnotation rcv, EAnnotation merged) { } - private def void deepCopy(ENamedElement context, List receiving, T merged) { + private def void deepCopy(ENamedElement context, EStructuralFeature f, List receiving, T merged) { receiving += EcoreUtil::copy(merged) context.EAnnotations += EcoreFactory.eINSTANCE.createEAnnotation => [ source = ORIGIN_ANNOTATION_SOURCE references += merged + references += f ] } - private def void doCollectionsMerge(ENamedElement context, List rcv, List merged) { + private def void doCollectionsMerge(ENamedElement context, EStructuralFeature f, List rcv, List merged) { merged.forEach[m | val match = rcv.findFirst[r | r.doMatch(m)] if (match !== null) match.doMerge(m) else - deepCopy(context, rcv, m) + deepCopy(context, f, rcv, m) ] } @@ -285,21 +291,22 @@ class PackageMergeMerger implements EcoreMerger { else #[a, b].max } - private def void addConflict(ENamedElement rcv, ENamedElement merged, String message) { - addConflict(rcv, merged, message, null) + private def void addConflict(ENamedElement rcv, ENamedElement merged, EStructuralFeature f, String message) { + addConflict(rcv, merged, f, message, null) } - private def void addConflict(ENamedElement rcv, ENamedElement merged, Diagnostic d) { + private def void addConflict(ENamedElement rcv, ENamedElement merged, EStructuralFeature f, Diagnostic d) { addConflict( rcv, merged, - '''Cannot merge «merged.uniqueId» with «rcv.uniqueId»: «d.message»''', + f, + '''Cannot merge «merged.uniqueId» with «rcv.uniqueId».«f.name»: «d.message»''', d ) } - private def void addConflict(ENamedElement rcv, ENamedElement merged, String message, Diagnostic d) { - conflicts += new Conflict(rcv, merged, message, d) + private def void addConflict(ENamedElement rcv, ENamedElement merged, EStructuralFeature f, String message, Diagnostic d) { + conflicts += new Conflict(rcv, merged, f, message, d) } override List getConflicts() { diff --git a/tests/fr.inria.diverse.melange.tests/src/main/java/fr/inria/diverse/melange/tests/EcoreMergerTest.xtend b/tests/fr.inria.diverse.melange.tests/src/main/java/fr/inria/diverse/melange/tests/EcoreMergerTest.xtend index 13b7600fd..21fb99ed0 100644 --- a/tests/fr.inria.diverse.melange.tests/src/main/java/fr/inria/diverse/melange/tests/EcoreMergerTest.xtend +++ b/tests/fr.inria.diverse.melange.tests/src/main/java/fr/inria/diverse/melange/tests/EcoreMergerTest.xtend @@ -122,9 +122,9 @@ class EcoreMergerTest assertNull(resulting) val conflicts = merger.conflicts assertEquals(2, conflicts.size) - assertEquals("Cannot merge ecore.EClass.id1 with ecore.EClass: The features 'id1' and 'id2' cannot both be IDs", + assertEquals("Cannot merge ecore.EClass.id1 with ecore.EClass.eStructuralFeatures: The features 'id1' and 'id2' cannot both be IDs", conflicts.get(0).message) - assertEquals("Cannot merge ecore.EClass.id2 with ecore.EClass: The features 'id1' and 'id2' cannot both be IDs", + assertEquals("Cannot merge ecore.EClass.id2 with ecore.EClass.eStructuralFeatures: The features 'id1' and 'id2' cannot both be IDs", conflicts.get(1).message) } @@ -135,7 +135,7 @@ class EcoreMergerTest assertNull(resulting) val conflicts = merger.conflicts assertEquals(1, conflicts.size) - assertEquals("Cannot merge ecore.EClass.abstract with ecore.EClass: There may not be two features named 'abstract'", + assertEquals("Cannot merge ecore.EClass.abstract with ecore.EClass.eStructuralFeatures: There may not be two features named 'abstract'", conflicts.head.message) } @@ -146,7 +146,7 @@ class EcoreMergerTest assertNull(resulting) val conflicts = merger.conflicts assertEquals(1, conflicts.size) - assertEquals("Cannot merge ecore.EClass.eSuperTypes with ecore.EClass: There may not be two features named 'eSuperTypes'", + assertEquals("Cannot merge ecore.EClass.eSuperTypes with ecore.EClass.eStructuralFeatures: There may not be two features named 'eSuperTypes'", conflicts.head.message) } @@ -157,7 +157,9 @@ class EcoreMergerTest assertNull(resulting) val conflicts = merger.conflicts assertEquals(2, conflicts.size) - assertTrue(conflicts.forall[message == "Conflicting containment values for reference"]) + conflicts.forEach[ + assertEquals("Conflicting containment values for reference", message) + ] } @Test @@ -167,7 +169,7 @@ class EcoreMergerTest assertNull(resulting) val conflicts = merger.conflicts assertEquals(1, conflicts.size) - assertTrue(conflicts.head.message.startsWith("Cannot merge ecore.EClass.getOperationID with ecore.EClass: There may not be two operations")) + assertTrue(conflicts.head.message.startsWith("Cannot merge ecore.EClass.getOperationID with ecore.EClass.eOperations: There may not be two operations")) } @Test @@ -194,7 +196,6 @@ class EcoreMergerTest def void testDerivedOverride() { val merged = getTestInput("DerivedOverride") val resulting = merger.merge(receivingEcore, merged) - println(merger.conflicts) assertNotNull(resulting) assertEquals(0, merger.conflicts.size) assertPkgEquals(resulting, getExpected("DerivedOverride")) @@ -210,6 +211,27 @@ class EcoreMergerTest assertMatch(resulting, getExpected("Ecore")) } + @Test + def void testValidSuperTypes() { + val merged = getTestInput("SuperTypes") + val resulting = merger.merge(receivingEcore, merged) + assertNotNull(resulting) + assertIsValid(resulting) + assertEquals(0, merger.conflicts.size) + assertPkgEquals(resulting, getExpected("SuperTypes")) + } + + @Test + def void testInvalidValidSuperTypes() { + val merged = getTestInput("InvalidSuperTypes") + val resulting = merger.merge(receivingEcore, merged) + assertNull(resulting) + val conflicts = merger.conflicts + assertEquals(2, conflicts.size) + assertEquals("Cannot merge ecore.EClass with ecore.EClassifier.eSuperTypes: A class may not be a super type of itself", conflicts.get(0).message) + assertEquals("Cannot merge ecore.EOperation with ecore.EModelElement.eSuperTypes: A class may not be a super type of itself", conflicts.get(1).message) + } + @Test def void testInvalidSelfMerge() { assertNull(merger.merge(receivingEcore, receivingEcore)) diff --git a/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/InvalidSuperTypes.ecore b/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/InvalidSuperTypes.ecore new file mode 100644 index 000000000..574462ba5 --- /dev/null +++ b/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/InvalidSuperTypes.ecore @@ -0,0 +1,8 @@ + + + + + + + diff --git a/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/SuperTypes.ecore b/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/SuperTypes.ecore new file mode 100644 index 000000000..822c8364e --- /dev/null +++ b/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/SuperTypes.ecore @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/expected/SuperTypes.ecore b/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/expected/SuperTypes.ecore new file mode 100644 index 000000000..982768477 --- /dev/null +++ b/tests/fr.inria.diverse.melange.tests/tests-inputs/metamodels/merge/expected/SuperTypes.ecore @@ -0,0 +1,531 @@ + + + + +
+ + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + +
+ + + + + + + +
+
+ + + + + + + + + + +
+
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+ + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+ + + + +
+
+ + + + + +
+ + + + +
+ + + + +
+ + + + +
+
+ + + + +
+ + + + +
+ + + + +
+
+ + + + + +
+
+ + + + + + +
+ + + + +
+
+ + + + + + + + + + +
+ + + + +
+
+ + + + +
+ + + + +
+
+ + + + + + + + +
+ + + + +
+
+ + + + + + + + + + +
+ + + + +
+
+ + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +