Skip to content

Commit

Permalink
ESuperTypes tests + improved error tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Degueule committed Sep 18, 2015
1 parent ef8b940 commit 36052b6
Show file tree
Hide file tree
Showing 5 changed files with 605 additions and 26 deletions.
Expand Up @@ -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 {
Expand All @@ -34,6 +36,7 @@ interface EcoreMerger {
static class Conflict {
EObject receiving
EObject merged
EStructuralFeature feature
String message
Diagnostic emfDiagnostic
}
Expand Down Expand Up @@ -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

Expand All @@ -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)
]
}
]
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -260,22 +265,23 @@ class PackageMergeMerger implements EcoreMerger {
private def dispatch void doMerge(EAnnotation rcv, EAnnotation merged) {
}

private def <T extends ENamedElement> void deepCopy(ENamedElement context, List<T> receiving, T merged) {
private def <T extends ENamedElement> void deepCopy(ENamedElement context, EStructuralFeature f, List<T> receiving, T merged) {
receiving += EcoreUtil::copy(merged)
context.EAnnotations += EcoreFactory.eINSTANCE.createEAnnotation => [
source = ORIGIN_ANNOTATION_SOURCE
references += merged
references += f
]
}

private def <T extends ENamedElement> void doCollectionsMerge(ENamedElement context, List<T> rcv, List<T> merged) {
private def <T extends ENamedElement> void doCollectionsMerge(ENamedElement context, EStructuralFeature f, List<T> rcv, List<T> 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)
]
}

Expand All @@ -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<Conflict> getConflicts() {
Expand Down
Expand Up @@ -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)
}

Expand All @@ -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)
}

Expand All @@ -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)
}

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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"))
Expand All @@ -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))
Expand Down
@@ -0,0 +1,8 @@
<?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="ecore" nsURI="http://supertypes/" nsPrefix="ecore">
<eClassifiers xsi:type="ecore:EClass" name="EClass"/>
<eClassifiers xsi:type="ecore:EClass" name="EClassifier" eSuperTypes="#//EClass"/>
<eClassifiers xsi:type="ecore:EClass" name="EOperation"/>
<eClassifiers xsi:type="ecore:EClass" name="EModelElement" eSuperTypes="#//EOperation"/>
</ecore:EPackage>
@@ -0,0 +1,11 @@
<?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="ecore" nsURI="http://supertypes/" nsPrefix="ecore">
<eClassifiers xsi:type="ecore:EClass" name="A" eSuperTypes="#//EOperation"/>
<eClassifiers xsi:type="ecore:EClass" name="B"/>
<eClassifiers xsi:type="ecore:EClass" name="C" eSuperTypes="#//B #//Y"/>
<eClassifiers xsi:type="ecore:EClass" name="EClass" eSuperTypes="#//C"/>
<eClassifiers xsi:type="ecore:EClass" name="EOperation"/>
<eClassifiers xsi:type="ecore:EClass" name="X" eSuperTypes="#//A"/>
<eClassifiers xsi:type="ecore:EClass" name="Y"/>
</ecore:EPackage>

0 comments on commit 36052b6

Please sign in to comment.