blob: 526a11ab3bad0de0b1b6ddb049879b87d3322e7e [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2013, 2017 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* CEA LIST - Initial API and implementation
* Christian W. Damus - bug 460716
*****************************************************************************/
package org.eclipse.papyrus.cdo.validation.problems.util.tests;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertThat;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.impl.EValidatorRegistryImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.papyrus.cdo.core.tests.AbstractPapyrusCDOTest;
import org.eclipse.papyrus.cdo.validation.problems.EProblem;
import org.eclipse.papyrus.cdo.validation.problems.ESeverity;
import org.eclipse.papyrus.cdo.validation.problems.ProblemsPackage;
import org.eclipse.papyrus.cdo.validation.problems.util.ProblemsManager;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.resource.UMLResource;
import org.eclipse.uml2.uml.util.UMLValidator;
import org.junit.After;
import org.junit.Test;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/**
* Tests suite for the {@link ProblemsManager} API.
*/
public class ProblemsManagerTest
extends AbstractPapyrusCDOTest {
private Set<ProblemsManager> managers = new java.util.HashSet<ProblemsManager>();
@Test
public void getProblemsAdapter() {
CDOTransaction transaction = createTransaction();
ProblemsManager mgr = getProblemsManager(transaction);
// doesn't create a new instance
assertThat(mgr,
sameInstance(ProblemsManager.getProblemsManager(transaction
.getResourceSet())));
}
@Test
public void allProblems() {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
createTestModel(resource);
final int expectedProblems = computeExpectedProblemsCount();
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
Collection<EProblem> allProblems = collect(mgr.getAllProblems());
assertThat(allProblems.size(), equalTo(expectedProblems));
assertIndistinguishableMembersProblem(resource, allProblems);
assertGeneralCompatibilityProblem(resource, allProblems);
assertUnownedElementProblem(resource, allProblems);
}
@Test
public void allProblems_EObject_noCrossReferencer() {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
createTestModel(resource);
final int expectedProblems = computeExpectedProblemsCount(getClass2());
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
Collection<EProblem> problems = collect(mgr
.getAllProblems(getClass2(resource)));
assertThat(problems.size(), equalTo(expectedProblems));
assertIndistinguishableMembersProblem(resource, problems);
assertGeneralCompatibilityProblem(resource, problems);
}
@Test
public void allProblems_EObject_crossReferencer() {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
// attach a cross-reference adapter
transaction.getResourceSet().eAdapters()
.add(new ECrossReferenceAdapter());
createTestModel(resource);
final int expectedProblems = computeExpectedProblemsCount(getClass2());
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
Collection<EProblem> problems = collect(mgr
.getAllProblems(getClass2(resource)));
assertThat(problems.size(), equalTo(expectedProblems));
assertIndistinguishableMembersProblem(resource, problems);
assertGeneralCompatibilityProblem(resource, problems);
}
@Test
public void purgeAll_noCrossReferencer() {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
createTestModel(resource);
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
mgr.purgeAllProblems();
assertThat(mgr.getAllProblems().hasNext(), is(false));
}
@Test
public void purgeAll_crossReferencer() {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
// attach a cross-reference adapter
transaction.getResourceSet().eAdapters()
.add(new ECrossReferenceAdapter());
createTestModel(resource);
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
mgr.purgeAllProblems();
assertThat(mgr.getAllProblems().hasNext(), is(false));
}
@Test
public void purge_noCrossReferencer() {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
createTestModel(resource);
final int expectedTotalProblems = computeExpectedProblemsCount();
final int expectedClass3Problems = computeExpectedProblemsCount(getClass3());
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
mgr.purgeProblems(getClass3(resource));
Collection<EProblem> problems = collect(mgr.getAllProblems());
assertThat(problems.size(), equalTo(expectedTotalProblems - expectedClass3Problems));
assertIndistinguishableMembersProblem(resource, problems);
assertGeneralCompatibilityProblem(resource, problems);
}
@Test
public void purge_crossReferencer() {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
// attach a cross-reference adapter
transaction.getResourceSet().eAdapters()
.add(new ECrossReferenceAdapter());
createTestModel(resource);
final int expectedTotalProblems = computeExpectedProblemsCount();
final int expectedClass3Problems = computeExpectedProblemsCount(getClass3());
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
mgr.purgeProblems(getClass3(resource));
Collection<EProblem> problems = collect(mgr.getAllProblems());
assertThat(problems.size(), equalTo(expectedTotalProblems - expectedClass3Problems));
assertIndistinguishableMembersProblem(resource, problems);
assertGeneralCompatibilityProblem(resource, problems);
}
@Test
public void disposeProblemsManager()
throws Exception {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
// attach a cross-reference adapter
ECrossReferenceAdapter xrefs = new ECrossReferenceAdapter();
transaction.getResourceSet().eAdapters().add(xrefs);
createTestModel(resource);
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
// gather weak references to the problems
ReferenceQueue<EProblem> queue = new ReferenceQueue<EProblem>();
List<Reference<EProblem>> references = new java.util.ArrayList<Reference<EProblem>>(
3);
for (Iterator<EProblem> iter = mgr.getAllProblems(); iter.hasNext();) {
references.add(new WeakReference<EProblem>(iter.next(), queue));
}
mgr.dispose();
// try to force GC
for (int i = 0; i < 10; i++) {
System.gc();
}
// assert that the problems are unreachable
for (int i = 0; i < 3; i++) {
Reference<? extends EProblem> ref = queue.remove(1000);
assertThat(ref, notNullValue());
}
}
@Test
public void dispose_crossReferencerAddedAfter()
throws Exception {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
createTestModel(resource);
ProblemsManager mgr = getProblemsManager(transaction);
mgr.addDiagnostic(validate(resource));
// attach a cross-reference adapter *after* adding problems
ECrossReferenceAdapter xrefs = new ECrossReferenceAdapter();
transaction.getResourceSet().eAdapters().add(xrefs);
// gather weak references to the problems
ReferenceQueue<EProblem> queue = new ReferenceQueue<EProblem>();
List<Reference<EProblem>> references = new java.util.ArrayList<Reference<EProblem>>(
3);
for (Iterator<EProblem> iter = mgr.getAllProblems(); iter.hasNext();) {
references.add(new WeakReference<EProblem>(iter.next(), queue));
}
mgr.dispose();
// try to force GC
for (int i = 0; i < 10; i++) {
System.gc();
}
// assert that the problems are unreachable
for (int i = 0; i < 3; i++) {
Reference<? extends EProblem> ref = queue.remove(1000);
assertThat(ref, notNullValue());
}
}
@Test
public void customProblemClass() {
CDOTransaction transaction = createTransaction();
CDOResource resource = transaction
.createResource(getResourcePath("/resource1"));
createTestModel(resource);
final int expectedProblems = computeExpectedProblemsCount();
// not actually a different class, but a different way of creating it
ProblemsManager mgr = ProblemsManager.createProblemsManager(
transaction.getResourceSet(), ProblemsPackage.Literals.EPROBLEM);
managers.add(mgr);
mgr.addDiagnostic(validate(resource));
Collection<EProblem> allProblems = collect(mgr.getAllProblems());
assertThat(allProblems.size(), equalTo(expectedProblems));
assertIndistinguishableMembersProblem(resource, allProblems);
assertGeneralCompatibilityProblem(resource, allProblems);
assertUnownedElementProblem(resource, allProblems);
}
//
// Test framework
//
@After
public void disposeProblemManagers()
throws Exception {
for (ProblemsManager next : managers) {
next.dispose();
}
}
void createTestModel(Resource res) {
Model model = UMLFactory.eINSTANCE.createModel();
model.setName("testmodel");
res.getContents().add(model);
Class class1 = model.createOwnedClass("Class1", false);
Interface interface1 = model.createOwnedInterface("Interface1");
Class class2 = model.createOwnedClass("Class2", false);
class2.createGeneralization(interface1); // invalid generalization
class2.createOwnedAttribute("attr1", class1);
class2.createOwnedAttribute("attr1", class1); // indistinguishable
Class class3 = UMLFactory.eINSTANCE.createClass();
class3.setName("Class3");
res.getContents().add(class3); // not owned
}
int computeExpectedProblemsCount() {
int result;
Resource resource = createScratchResource();
try {
Diagnostic diagnostic = validate(resource);
result = diagnostic.getChildren().size();
} finally {
unload(resource);
}
return result;
}
int computeExpectedProblemsCount(Function<Resource, ? extends EObject> pertainingTo) {
int result;
Resource resource = createScratchResource();
final EObject target = pertainingTo.apply(resource);
try {
Diagnostic diagnostic = validate(resource);
result = Iterables.size(Iterables.filter(diagnostic.getChildren(), new Predicate<Diagnostic>() {
@Override
public boolean apply(Diagnostic input) {
List<?> data = input.getData();
return (data != null) && !data.isEmpty() && (data.get(0) == target);
}
}));
} finally {
unload(resource);
}
return result;
}
Resource createScratchResource() {
Resource result = UMLResource.Factory.INSTANCE.createResource(URI.createURI("bogus://scratch.uml"));
createTestModel(result);
return result;
}
void unload(Resource resource) {
resource.unload();
if (resource.getResourceSet() != null) {
resource.getResourceSet().getResources().remove(resource);
}
resource.eAdapters().clear();
}
Model getModel(Resource resource) {
return (Model) EcoreUtil.getObjectByType(resource.getContents(),
UMLPackage.Literals.MODEL);
}
Class getClass1(Resource resource) {
return getClass1().apply(resource);
}
Function<Resource, Class> getClass1() {
return getNamedClass("Class1");
}
Function<Resource, Class> getNamedClass(final String name) {
return new Function<Resource, Class>() {
@Override
public Class apply(Resource input) {
return (Class) getModel(input).getOwnedType(name);
}
};
}
Class getClass2(Resource resource) {
return getClass2().apply(resource);
}
Function<Resource, Class> getClass2() {
return getNamedClass("Class2");
}
Class getClass3(Resource resource) {
return getClass3().apply(resource);
}
Function<Resource, Class> getClass3() {
return getRoot(Class.class);
}
<T extends EObject> Function<Resource, T> getRoot(final java.lang.Class<T> metaclass) {
return new Function<Resource, T>() {
@Override
public T apply(Resource input) {
return Iterables.getFirst(Iterables.filter(input.getContents(), metaclass), null);
}
};
}
Interface getInterface1(Resource resource) {
return (Interface) getModel(resource).getOwnedType("Interface1");
}
ProblemsManager getProblemsManager(CDOView view) {
return getProblemsManager(view.getResourceSet());
}
ProblemsManager getProblemsManager(ResourceSet resourceSet) {
ProblemsManager result = ProblemsManager
.getProblemsManager(resourceSet);
managers.add(result);
return result;
}
Diagnostic validate(Resource resource) {
EValidator.Registry registry = new EValidatorRegistryImpl(
EValidator.Registry.INSTANCE);
registry.put(UMLPackage.eINSTANCE, new UMLValidator());
BasicDiagnostic result = new BasicDiagnostic();
Diagnostician diagnostician = new Diagnostician(registry);
for (EObject next : resource.getContents()) {
diagnostician.validate(next, result);
}
return result;
}
Collection<EProblem> collect(Iterator<? extends EProblem> problems) {
Collection<EProblem> result = new java.util.ArrayList<EProblem>();
while (problems.hasNext()) {
result.add(problems.next());
}
return result;
}
void assertProblem(Collection<EProblem> problems, ESeverity severity,
String source, int code, String messagePattern, EObject element,
EObject... related) {
final List<EObject> relatedList = Arrays.asList(related);
final Pattern pattern = Pattern.compile(messagePattern);
EProblem found = null;
for (EProblem problem : problems) {
if (problem.getElement() == element //
&& problem.getRelated().equals(relatedList) //
&& problem.getSeverity() == severity //
&& pattern.matcher(problem.getMessage()).find() //
&& source.equals(problem.getSource()) //
&& problem.getCode() == code) {
found = problem;
break;
}
}
assertThat(found, notNullValue());
}
void assertIndistinguishableMembersProblem(Resource resource,
Collection<EProblem> problems) {
Class class2 = getClass2(resource);
assertProblem(problems, ESeverity.WARNING,
UMLValidator.DIAGNOSTIC_SOURCE,
UMLValidator.NAMESPACE__MEMBERS_DISTINGUISHABLE, "disting", class2);
}
void assertGeneralCompatibilityProblem(Resource resource,
Collection<EProblem> problems) {
Class class2 = getClass2(resource);
Interface interface1 = getInterface1(resource);
assertProblem(problems, ESeverity.WARNING,
UMLValidator.DIAGNOSTIC_SOURCE,
UMLValidator.CLASSIFIER__SPECIALIZE_TYPE, "special", class2,
interface1);
}
void assertUnownedElementProblem(Resource resource,
Collection<EProblem> problems) {
Class class3 = getClass3(resource);
assertProblem(problems, ESeverity.WARNING,
UMLValidator.DIAGNOSTIC_SOURCE, UMLValidator.ELEMENT__HAS_OWNER,
"owned", class3);
}
}