blob: 803cde2f5b9698579b863c5d4bc310cca7930ef9 [file] [log] [blame]
/*******************************************************************************
* Copyright 2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* All rights reserved. This program and the accompanying materials
* are made available under the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
******************************************************************************/
package org.eclipse.emf.emfstore.client.test.integration.forward;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EPackage.Registry;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.emfstore.client.model.Configuration;
import org.eclipse.emf.emfstore.client.model.ModelFactory;
import org.eclipse.emf.emfstore.client.model.ProjectSpace;
import org.eclipse.emf.emfstore.client.model.WorkspaceManager;
import org.eclipse.emf.emfstore.client.model.impl.ProjectSpaceImpl;
import org.eclipse.emf.emfstore.client.model.util.EMFStoreCommand;
import org.eclipse.emf.emfstore.common.model.Project;
import org.eclipse.emf.emfstore.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.server.model.ProjectId;
import org.eclipse.emf.emfstore.server.model.versioning.ChangePackage;
import org.eclipse.emf.emfstore.server.model.versioning.PrimaryVersionSpec;
import org.eclipse.emf.emfstore.server.model.versioning.VersioningFactory;
import org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.CreateDeleteOperation;
/**
* Helper class for testing.
*
* @author Hodaie
*/
public final class IntegrationTestHelper {
/**
* Numbers of different integration tests. Each integration test case must have a corresponding method in this class
* which actually implements the test and will be called from test case. Knowing the number of available methods is
* required in composite test.
*/
public static final int NUM_OF_TESTS = 16;
private static EditingDomain domain;
private static final String TEMP_PATH = Configuration.getWorkspaceDirectory() + "tmp";
private Random random;
private Set<EObject> allMEsInProject;
private Project testProject;
/**
* Constructor.
*
* @param randomSeed random seed
* @param testProject test project
*/
public IntegrationTestHelper(long randomSeed, Project testProject) {
this.random = new Random(randomSeed);
this.testProject = testProject;
}
/**
* Creates an empty project space.
*
* @param name project space name
* @return an empty project space
*/
public static ProjectSpace createEmptyProjectSpace(String name) {
ProjectSpace projectSpace = ModelFactory.eINSTANCE.createProjectSpace();
ProjectId projectId = org.eclipse.emf.emfstore.server.model.ModelFactory.eINSTANCE.createProjectId();
projectId.setId(name);
projectSpace.setIdentifier(name);
projectSpace.setProjectId(projectId);
projectSpace.setProjectName(name);
projectSpace.setProjectDescription("description");
PrimaryVersionSpec versionSpec = VersioningFactory.eINSTANCE.createPrimaryVersionSpec();
versionSpec.setIdentifier(0);
projectSpace.setBaseVersion(versionSpec);
projectSpace.setLastUpdated(new Date());
projectSpace.setUsersession(null);
// projectSpace.setProject(ModelFactory.eINSTANCE.createProject());
projectSpace.setResourceCount(0);
File file = new File(TEMP_PATH);
if (!file.exists()) {
new File(TEMP_PATH).mkdir();
}
return projectSpace;
}
/**
* Creates a change package from given operations.
*
* @param operations list of operations. This can be obtained by calling ProjectSpace.getOperations() method.
* @param cannonize if operations will be cannonized.
* @param clearOperations if operations must be cleared from project space. This is used when running more than one
* change package test after each other and we don't want operation from one change package test to be
* accumulated in operations of change package test run after that.
* @return a change package created from given operations
*/
public static ChangePackage getChangePackage(final List<AbstractOperation> operations, final boolean cannonize,
final boolean clearOperations) {
final ChangePackage changePackage = VersioningFactory.eINSTANCE.createChangePackage();
new EMFStoreCommand() {
@Override
protected void doRun() {
for (AbstractOperation op : operations) {
changePackage.getOperations().add(ModelUtil.clone(op));
}
if (clearOperations) {
operations.clear();
}
if (cannonize) {
changePackage.cannonize();
}
}
}.run(false);
return changePackage;
}
/**
* This method compares two projects using {@link ModelUtil#areEqual(EObject, EObject)}. You can also use
* linearCompare(project, project) to identify the position in which the project differ.
*
* @param testSpace test project space
* @param compareSpace compare project space
* @return if objects are equal
*/
public static boolean compare(ProjectSpace testSpace, ProjectSpace compareSpace) {
// extract changes from testSpace and apply them to compareSpace
prepareCompare(testSpace, compareSpace, false);
System.out.println("comparing...");
return ModelUtil.areEqual(testSpace.getProject(), compareSpace.getProject());
}
/**
* Extracts changes form test project and applies them to compare project. Extracted ChangePackage is also written
* to disk (to TMP_PATH/changePackage.txt file) for further investigations.
*
* @param testSpace project space to extract operations from
* @param compareSpace project space to apply extracted operations to
* @param accumulative if operations must remain in test project space after extracting them
*/
private static void prepareCompare(final ProjectSpace testSpace, final ProjectSpace compareSpace,
final boolean accumulative) {
System.out.println("extracting operations from test project...");
List<AbstractOperation> operations = testSpace.getOperations();
System.out.println(operations.size() + " operatoins");
final ChangePackage changePackage = getChangePackage(operations, true, false);
// Save change package for later reference to disk.
// The saved change package will be overwritten every time a test
// succeeds.
EObject copyChangePackage = ModelUtil.clone(changePackage);
ResourceSet reseourceSet = new ResourceSetImpl();
Resource resource = reseourceSet.createResource(URI.createFileURI(TEMP_PATH + File.separator
+ "changePackage.txt"));
resource.getContents().add(copyChangePackage);
try {
resource.save(ModelUtil.getResourceSaveOptions());
} catch (IOException e) {
e.printStackTrace();
}
// apply changes to compare project
new EMFStoreCommand() {
@Override
protected void doRun() {
System.out.println("applying changes to compareSpace...");
((ProjectSpaceImpl) compareSpace).stopChangeRecording();
changePackage.apply(compareSpace.getProject());
if (!accumulative) {
testSpace.getOperations().clear();
}
}
}.run(false);
}
/**
* Returns editing domain.
*
* @return editing domain
*/
public static EditingDomain getDomain() {
if (domain == null) {
domain = Configuration.getEditingDomain();
}
return domain;
}
/**
* Returns a list of randomly selected MEs.
*
* @param project project from which to select MEs
* @param num how many MEs to select
* @param unique if they must be unique
* @return a random list of MEs
*/
public List<EObject> getRandomMEs(Project project, int num, boolean unique) {
List<EObject> result = new ArrayList<EObject>();
if (allMEsInProject == null) {
System.out.println("getting list of all model elements in project...");
allMEsInProject = project.getAllModelElements();
System.out.println(allMEsInProject.size() + " MEs in project...");
}
int numOfMEs = allMEsInProject.size();
if (num > numOfMEs) {
throw new IllegalArgumentException(
"Number of random MEs to return is greater than total number of MEs in project.");
}
if (unique) {
do {
final EObject me = (EObject) allMEsInProject.toArray()[getRandom().nextInt(numOfMEs - 1)];
if (!result.contains(me)) {
result.add(me);
}
} while (result.size() < num);
} else {
for (int i = 0; i < num; i++) {
final EObject me = (EObject) allMEsInProject.toArray()[getRandom().nextInt(numOfMEs - 1)];
result.add(me);
}
}
return result;
}
/**
* Returns a randomly selected ME form this project.
*
* @param project project
* @return randomly selected ME
*/
public EObject getRandomME(Project project) {
List<EObject> modelElements = getRandomMEs(project, 1, false);
return modelElements.get(0);
}
/**
* Returns a randomly selected ME of given type from this project. If there is no ME of this type in project null is
* returned.
*
* @param project project
* @param type model element type
* @return ME or null if there is no ME of this type in project
*/
public EObject getRandomMEofType(Project project, EClass type) {
List<EObject> refTypeMEs = project.getAllModelElementsbyClass(type, new BasicEList<EObject>());
int size = refTypeMEs.size();
if (size == 0) {
return null;
// throw new IllegalStateException("There is no ME of this type in Project: " + type.getName());
}
EObject me = refTypeMEs.get(getRandomPosition(size));
return me;
}
/**
* Creates an instance of model element with a randomly selected model element type.
*
* @return ME
*/
public EObject createRandomME() {
Set<EClass> eClazzSet = getSubclasses(EcoreFactory.eINSTANCE.getEcorePackage().getEObject());
ArrayList<EClass> eClazz = new ArrayList<EClass>(eClazzSet);
EClass eClass = eClazz.get(getRandom().nextInt(eClazz.size() - 1));
EObject me = eClass.getEPackage().getEFactoryInstance().create(eClass);
return me;
}
/**
* Returns all non-abstract, non-interface subclasses of the given input class in the given package. In other words
* returns all classes that have direct instances.
*
* @param clazz the eClass, must be a subtype of ModelElement
* @return a set of EClasses IMPORTANT: Will throw an IllegalArgumentException if given EClass is not a subtype of
* ModelElement
*/
public static Set<EClass> getSubclasses(EClass clazz) {
return getSubclasses(clazz, false);
}
/**
* Returns all subclasses of the given input class in the given package.
*
* @param clazz the eClass, must be a subtype of ModelElement
* @param includeAbstractClassesAndInterfaces true if interfaces and abstract classes should be included in the
* result
* @return a set of EClasses IMPORTANT: Will throw an IllegalArgumentException if given EClass is not a subtype of
* ModelElement
*/
public static Set<EClass> getSubclasses(EClass clazz, boolean includeAbstractClassesAndInterfaces) {
// sanity checks
// EClass modelELementEClass = MetamodelPackage.eINSTANCE.getModelElement();
// if (!modelELementEClass.isSuperTypeOf(clazz)) {
// throw new IllegalStateException("Given EClass \"" + clazz.getName()
// + "\" is not a subtype of EClass ModelElement");
// }
Set<EClass> ret = new HashSet<EClass>();
for (EPackage ePackage : getAllModelPackages()) {
getSubclasses(clazz, ret, ePackage, includeAbstractClassesAndInterfaces);
}
return ret;
}
private static void getSubclasses(EClass clazz, Set<EClass> ret, EPackage ePackage,
boolean includeAbstractClassesAndInterfaces) {
for (EClassifier classifier : ePackage.getEClassifiers()) {
if (EcorePackage.eINSTANCE.getEClass().isInstance(classifier)) {
EClass subClass = (EClass) classifier;
if ((clazz.isSuperTypeOf(subClass) || clazz == EcorePackage.eINSTANCE.getEObject())
&& (includeAbstractClassesAndInterfaces || canHaveInstances(subClass))) {
ret.add(subClass);
}
}
}
for (EPackage subPackage : ePackage.getESubpackages()) {
getSubclasses(clazz, ret, subPackage, includeAbstractClassesAndInterfaces);
}
}
private static boolean canHaveInstances(EClass eClass) {
return !(eClass.isAbstract() || eClass.isInterface());
}
/**
* Retrieve all EPackages that are model packages for unicase starting with the unicase model prefix as defined in
* {@link MetamodelPackage}.
*
* @return a set of EPackages
*/
public static Set<EPackage> getAllModelPackages() {
Set<EPackage> result = new HashSet<EPackage>();
Registry registry = EPackage.Registry.INSTANCE;
for (Entry<String, Object> entry : registry.entrySet()) {
// if (entry.getKey().startsWith(ModelPackage..MODEL_URL_PREFIX)) {
// try {
EPackage model = EPackage.Registry.INSTANCE.getEPackage(entry.getKey());
result.add(model);
// }
// BEGIN SUPRESS CATCH EXCEPTION
// catch (RuntimeException exception) {
// END SUPRESS CATCH EXCEPTION
// logException("Failed to load model package " + entry.getKey(), exception);
// }
// }
}
return result;
}
/**
* This creates a mew model element of given type. If type is abstract or interface a randomly selected sub-type of
* it will be instantiated.
*
* @param refType EReference type
* @return a new model element instance of refType
*/
public EObject createInstance(EClass refType) {
EObject ret = null;
if (refType.isAbstract() || refType.isInterface()) {
List<EClass> eClazz = new ArrayList<EClass>(getSubclasses(refType));
int index = getRandomPosition(eClazz.size());
refType = eClazz.get(index);
}
ret = refType.getEPackage().getEFactoryInstance().create(refType);
return ret;
}
/**
* This changes the given attribute on given ME. If attribute isMany then a new entry of attribute type will be
* added to a random position of its list. Also if attribute is an Enum, then a new value from this enumeration will
* be set for it. Note that new values are all selected randomly (except for strings).
*
* @param me model element
* @param attribute attribute to change
*/
@SuppressWarnings("unchecked")
public void changeAttribute(EObject me, EAttribute attribute) {
if (attribute.getEType().getInstanceClass().equals(String.class)) {
if (attribute.isMany()) {
Object object = me.eGet(attribute);
EList<String> eList = (EList<String>) object;
int position = getRandomPosition(eList.size());
eList.add(position, "new entry for" + attribute.getName());
} else {
String oldValue = (String) me.eGet(attribute);
String newValue = "changed-" + oldValue;
me.eSet(attribute, newValue);
}
} else if (attribute.getEType().getInstanceClass().equals(boolean.class)) {
if (attribute.isMany()) {
Object object = me.eGet(attribute);
EList<Boolean> eList = (EList<Boolean>) object;
int position = getRandomPosition(eList.size());
eList.add(position, getRandom().nextBoolean());
} else {
me.eSet(attribute, !((Boolean) me.eGet(attribute)));
}
} else if (attribute.getEType().getInstanceClass().equals(int.class)) {
if (attribute.isMany()) {
Object object = me.eGet(attribute);
EList<Integer> eList = (EList<Integer>) object;
int position = getRandomPosition(eList.size());
eList.add(position, getRandom().nextInt());
} else {
me.eSet(attribute, getRandom().nextInt());
}
} else if (attribute.getEType().getInstanceClass().equals(Date.class)) {
if (attribute.isMany()) {
Object object = me.eGet(attribute);
EList<Date> eList = (EList<Date>) object;
int position = getRandomPosition(eList.size());
eList.add(position, getRandomDate());
} else {
me.eSet(attribute, getRandomDate());
}
}
if (attribute.getEType() instanceof EEnum) {
EEnum en = (EEnum) attribute.getEType();
int numOfLiterals = en.getELiterals().size();
int index = getRandomPosition(numOfLiterals);
EEnumLiteral value = en.getELiterals().get(index);
me.eSet(attribute, value.getInstance());
}
}
/**
* Returns a random position in a list. If list size is 0, 0 is returned.
*
* @param listSize list size
* @return random index
*/
public int getRandomPosition(int listSize) {
int position;
if (listSize == 0) {
position = 0;
} else {
position = getRandom().nextInt(listSize);
}
return position;
}
/**
* Returns random generator of this TestHelper.
*
* @return random
*/
public Random getRandom() {
return random;
}
/**
* get a random date.
*
* @return ranodm date
*/
private static Date getRandomDate() {
return new Date();
}
/**
* Returns randomly one of attributes of this ME. The returned attribute is changeable, is not
* MODEL_ELEMENT_IDENTIFIER, and is not transient. Returns null if this ME does not have any attributes.
*
* @param me ME
* @return a random selected attribute
*/
public EAttribute getRandomAttribute(EObject me) {
EAttribute attribute = null;
List<EAttribute> attributes = new ArrayList<EAttribute>();
for (EAttribute tmpAttr : me.eClass().getEAllAttributes()) {
if (tmpAttr.isChangeable() && !tmpAttr.isTransient()) {
attributes.add(tmpAttr);
}
}
int size = attributes.size();
if (size != 0) {
attribute = attributes.get(getRandomPosition(size));
}
return attribute;
}
/**
* returns a random reference of this ME. Returns null if this ME does not have any references.
*
* @param me model element
* @return random reference of this ME
*/
public EReference getRandomReference(EObject me) {
int size = me.eClass().getEAllReferences().size();
int position = getRandomPosition(size);
EReference ref = null;
if (size != 0) {
ref = me.eClass().getEAllReferences().get(position);
}
return ref;
}
/**
* Returns a randomly selected non-containment reference of this ME. It is changeable, and not transient. Returns
* null if this ME does not have any matching references.
*
* @param me ME
* @return randomly selected changeable non-transient non-containment reference of this ME
*/
public EReference getRandomNonContainmentRef(EObject me) {
EReference nonContainmentRef = null;
List<EReference> nonContainmentRefs = new ArrayList<EReference>();
for (EReference ref : me.eClass().getEAllReferences()) {
if (!ref.isContainment() && !ref.isContainer() && ref.isChangeable() && !ref.isTransient()) {
nonContainmentRefs.add(ref);
}
}
int size = nonContainmentRefs.size();
if (size != 0) {
nonContainmentRef = nonContainmentRefs.get(getRandomPosition(size));
}
return nonContainmentRef;
}
/**
* Returns a randomly selected containment reference of this ME. It is changeable and not transient. Returns null if
* this ME does not have any matching references.
*
* @param me ME
* @return a randomly selected containment reference of this ME. It is changeable and not transient
*/
public EReference getRandomContainmentRef(EObject me) {
EReference containmentRef = null;
List<EReference> containments = new ArrayList<EReference>();
for (EReference ref : me.eClass().getEAllContainments()) {
if (ref.isChangeable() && !ref.isTransient()) {
containments.add(ref);
}
}
int size = containments.size();
if (size != 0) {
containmentRef = containments.get(getRandomPosition(size));
}
return containmentRef;
}
/**
* This method takes a model element and a non-containment reference. It gathers all model elements in project which
* have the type of that reference. Takes one of these in random, and adds it to reference list of input model
* element (in a random position if ref is many).
*
* @param me the input model element
* @param ref the non-containment reference to change
* @param project the project
* @return model element which is added to this reference
*/
@SuppressWarnings("unchecked")
public EObject changeNonContainementRef(EObject me, EReference ref, Project project) {
EClass refType = ref.getEReferenceType();
List<EObject> refTypeMEs = project.getAllModelElementsbyClass(refType, new BasicEList<EObject>());
if (refTypeMEs.contains(me)) {
refTypeMEs.remove(me);
}
EObject toBeReferencedME = refTypeMEs.get(getRandomPosition(refTypeMEs.size()));
Object object = me.eGet(ref);
if (ref.isMany()) {
EList<EObject> eList = (EList<EObject>) object;
if (eList == null) {
throw new IllegalStateException("Null list return for feature " + ref.getName() + " on "
+ ModelUtil.getProject(me).getModelElementId(me).getId());
} else {
int position = getRandomPosition(eList.size());
eList.add(position, toBeReferencedME);
}
} else {
me.eSet(ref, toBeReferencedME);
}
return toBeReferencedME;
}
/**
* Adds the given model element to this non-containment reference of given ME. If reference is not many, the given
* model element replaces the previous value of this reference.
*
* @param me me model element to change its reference
* @param ref non-containment reference
* @param toBeReferencedME model element to reference
*/
@SuppressWarnings("unchecked")
public void changeReference(EObject me, EReference ref, EObject toBeReferencedME) {
Object object = me.eGet(ref);
if (ref.isMany()) {
EList<EObject> eList = (EList<EObject>) object;
if (eList == null) {
throw new IllegalStateException("Null list return for feature " + ref.getName() + " on "
+ ModelUtil.getProject(me).getModelElementId(me).getId());
} else {
int position = getRandomPosition(eList.size());
if (!eList.contains(toBeReferencedME)) {
eList.add(position, toBeReferencedME);
}
}
} else {
me.eSet(ref, toBeReferencedME);
}
}
/**
* Changes the index of a value in a multi-valued EAtrribute of this ME.
*
* @param me ME model element to change
* @param attribute an attribute with multiple values (isMany = true)
*/
public void moveMultiAttributeValue(EObject me, EAttribute attribute) {
if (!attribute.isMany()) {
throw new IllegalArgumentException("Given attribute must be multiple valued (isMany = true)");
}
Object object = me.eGet(attribute);
EList<?> eList = (EList<?>) object;
int position1 = getRandomPosition(eList.size());
int position2 = getRandomPosition(eList.size());
if (position1 == position2) {
return;
}
Collections.swap(eList, position1, position2);
}
/**
* Changes index of a reference in a many EReference of given ME.
*
* @param me ME
* @param ref EReference to change
*/
@SuppressWarnings("unchecked")
public void moveMultiReferenceValue(EObject me, EReference ref) {
if (!ref.isMany()) {
throw new IllegalArgumentException("Given reference must be multiple valued (isMany = true)");
}
Object object = me.eGet(ref);
EList<EObject> eList = (EList<EObject>) object;
if (eList == null) {
throw new IllegalStateException("Null list return for feature " + ref.getName() + " on "
+ ModelUtil.getProject(me).getModelElementId(me).getId());
} else {
int position1 = getRandomPosition(eList.size());
int position2 = getRandomPosition(eList.size());
if (position1 == position2) {
return;
}
Collections.swap(eList, position1, position2);
}
}
/**
* @return the testProject
*/
public Project getTestProject() {
return testProject;
}
/**
* Takes a random ME (meA). Takes randomly one of its containment references. Creates a new ME matching containment
* reference type (meB). Adds created meB to meA's containment reference.
*/
@SuppressWarnings("unchecked")
public void doContainemntReferenceAddNew() {
// get a random ME and one of its containment references
EObject me = getRandomME(testProject);
EReference refToChange = getRandomContainmentRef(me);
// Maybe this ME does not have any containment references. Then choose another one.
while (refToChange == null) {
me = getRandomME(testProject);
refToChange = getRandomContainmentRef(me);
}
EClass refType = refToChange.getEReferenceType();
// create a new instance of reference type
EObject newInstance = createInstance(refType);
if (newInstance == null) {
throw new IllegalStateException("could not create a model element of specified type.");
}
Object object = me.eGet(refToChange);
if (refToChange.isMany()) {
EList<EObject> eList = (EList<EObject>) object;
if (eList == null) {
throw new IllegalStateException("Null list return for feature " + refToChange.getName() + " on "
+ ModelUtil.getProject(me).getModelElementId(me).getId());
} else {
int position = getRandomPosition(eList.size());
eList.add(position, newInstance);
}
} else {
me.eSet(refToChange, newInstance);
}
}
/**
* 1. Get a random model element form test project; 2. get randomly one of its attributes. 3. change the attribute
*/
public void doChangeAttribute() {
EObject me = getRandomME(getTestProject());
EAttribute attributeToChange = getRandomAttribute(me);
changeAttribute(me, attributeToChange);
}
/**
* Select a random ME (meA). Select one of its non-containment references. Find an ME matching reference type (meB).
* Add meB to meA.
*/
public void doNonContainmentReferenceAdd() {
EObject meToReference = null;
EObject me = null;
EReference refToChange = null;
while (meToReference == null) {
me = getRandomME(getTestProject());
refToChange = getRandomNonContainmentRef(me);
while (refToChange == null) {
me = createRandomME();
refToChange = getRandomNonContainmentRef(me);
}
meToReference = getRandomMEofType(getTestProject(), refToChange.getEReferenceType());
}
changeReference(me, refToChange, meToReference);
}
/**
* Change the same attribute on a randomly selected ME twice.
*/
public void doAttributeTransitiveChange() {
EObject me = getRandomME(getTestProject());
EAttribute attributeToChange = getRandomAttribute(me);
// from unset or a to b
changeAttribute(me, attributeToChange);
// from b to c
changeAttribute(me, attributeToChange);
}
/**
* This move an element in a many reference list to another position.
*/
public void doMultiReferenceMove() {
EObject me = getRandomME(getTestProject());
EReference refToChange = getRandomReference(me);
while (refToChange == null || !refToChange.isMany()) {
me = getRandomME(getTestProject());
refToChange = getRandomReference(me);
}
moveMultiReferenceValue(me, refToChange);
}
/**
* Delete a random ME.
*/
public void doDelete() {
EObject me = getRandomME(getTestProject());
ModelUtil.getProject(me).deleteModelElement(me);
}
/**
*
*/
public void doContainmentReferenceMove() {
EObject meToMove = null;
EObject me = null;
EReference refToChange = null;
while (meToMove == null) {
// get a random ME and one of its containment references (refToChange)
me = getRandomME(testProject);
refToChange = getRandomContainmentRef(me);
// Maybe this ME does not have any containment references. Then choose another one.
while (refToChange == null) {
me = getRandomME(testProject);
refToChange = getRandomContainmentRef(me);
}
// get a random ME matching refToChange. Also take care that meToMove is not the same as or an ancestor of
// ME
meToMove = getRandomMEofType(getTestProject(), refToChange.getEReferenceType());
if (meToMove != null && (meToMove.equals(me) || EcoreUtil.isAncestor(meToMove, me))) {
meToMove = null;
}
}
changeReference(me, refToChange, meToMove);
}
/**
* This takes a random model element (meA). Takes one of its containments (meToMove). Takes containing reference of
* meToMove. Finds another ME of type meA (meB). Moves meToMove to meB. Finds yet another ME of type meA (meC) .
* Moves meToMove to meC.
*/
public void doContainmentRefTransitiveChange() {
EObject meA = null;
EObject meB = null;
EObject meC = null;
EObject meToMove = null;
EReference refToChange = null;
while (refToChange == null || meB == null || meC == null) {
while (meToMove == null) {
// get a random ME and one of its containment references (refToChange)
meA = getRandomME(testProject);
int contentsSize = meA.eContents().size();
if (contentsSize != 0) {
meToMove = meA.eContents().get(getRandomPosition(contentsSize));
}
}
refToChange = meToMove.eContainmentFeature();
meB = getRandomMEofType(testProject, meA.eClass());
meC = getRandomMEofType(testProject, meA.eClass());
if (meA.equals(meB) || meA.equals(meC) || meB.equals(meC)) {
refToChange = null;
}
if (!sanityCheckContainmentRefTransitiveChange(meB, meC, meToMove, refToChange)) {
refToChange = null;
}
}
changeReference(meB, refToChange, meToMove);
changeReference(meC, refToChange, meToMove);
}
/**
* @param meA
* @param meB
* @param meC
* @param meToMove
* @param refToChange
* @return
*/
private boolean sanityCheckContainmentRefTransitiveChange(EObject meB, EObject meC, EObject meToMove,
EReference refToChange) {
if (meToMove == null) {
return false;
}
if (meToMove.equals(meB) || EcoreUtil.isAncestor(meToMove, meB)) {
return false;
}
if (meToMove.equals(meC) || EcoreUtil.isAncestor(meToMove, meC)) {
return false;
}
return true;
}
/**
* create a random ME and change one of its attributes.
*/
public void doCreateAndChangeAttribute() {
EObject me = createRandomME();
EAttribute attributeToChange = getRandomAttribute(me);
while (attributeToChange == null) {
me = createRandomME();
attributeToChange = getRandomAttribute(me);
}
testProject.getModelElements().add(me);
changeAttribute(me, attributeToChange);
}
/**
* Create a random ME and change one of its non-containment references.
*/
public void doCreateAndChangeRef() {
EObject meToReference = null;
EObject me = null;
EReference refToChange = null;
while (meToReference == null) {
me = createRandomME();
refToChange = getRandomNonContainmentRef(me);
while (refToChange == null) {
me = createRandomME();
refToChange = getRandomNonContainmentRef(me);
}
meToReference = getRandomMEofType(getTestProject(), refToChange.getEReferenceType());
}
testProject.getModelElements().add(me);
changeReference(me, refToChange, meToReference);
}
/**
* Create a random ME and change one of its attributes, then changes one of its references, then changes one of its
* attributes, and again changes one of its references.
*/
public void doCreateAndMultipleChange() {
EObject me = null;
EAttribute attr1 = null;
EAttribute attr2 = null;
EReference ref1 = null;
EReference ref2 = null;
EObject meToRef1 = null;
EObject meToRef2 = null;
while (!sanityCheckCreateAndMultipleChange(me, ref1, meToRef1, ref2, meToRef2)) {
meToRef1 = null;
meToRef2 = null;
me = createRandomME();
ref1 = getRandomReference(me);
ref2 = getRandomReference(me);
attr1 = getRandomAttribute(me);
attr2 = getRandomAttribute(me);
if (ref1 == null || ref2 == null || attr1 == null || attr2 == null) {
continue;
}
meToRef1 = getRandomMEofType(getTestProject(), ref1.getEReferenceType());
meToRef2 = getRandomMEofType(getTestProject(), ref2.getEReferenceType());
}
getTestProject().getModelElements().add(me);
changeAttribute(me, attr1);
changeReference(me, ref1, meToRef1);
changeAttribute(me, attr2);
changeReference(me, ref2, meToRef2);
}
/**
* @param me
* @param ref1
* @param meToRef1
* @param ref2
* @param meToRef2
* @return
*/
private boolean sanityCheckCreateAndMultipleChange(EObject me, EReference ref1, EObject meToRef1, EReference ref2,
EObject meToRef2) {
if (ref1 == null || ref2 == null) {
return false;
}
if (meToRef1 == null || meToRef2 == null) {
return false;
}
if (ref1.isContainment() && (meToRef1.equals(me) || EcoreUtil.isAncestor(meToRef1, me))) {
return false;
}
if (ref2.isContainment() && (meToRef2.equals(me) || EcoreUtil.isAncestor(meToRef2, me))) {
return false;
}
return true;
}
/**
* Create a random ME. Change one of its non-containment references. Delete ME.
*/
public void doCreateChangeRefDelete() {
EObject meToReference = null;
EObject me = null;
EReference refToChange = null;
while (meToReference == null) {
me = createRandomME();
refToChange = getRandomNonContainmentRef(me);
while (refToChange == null) {
me = createRandomME();
refToChange = getRandomNonContainmentRef(me);
}
meToReference = getRandomMEofType(getTestProject(), refToChange.getEReferenceType());
}
getTestProject().getModelElements().add(me);
changeReference(me, refToChange, meToReference);
ModelUtil.getProject(me).deleteModelElement(me);
}
/**
* Create a random ME. Delete ME.
*/
public void doCreateDelete() {
EObject me = createRandomME();
getTestProject().getModelElements().add(me);
getTestProject().deleteModelElement(me);
}
/**
* Delete a random ME. Revert delete.
*/
public void doDeleteAndRevert() {
final EObject modelElement = getRandomME(getTestProject());
new EMFStoreCommand() {
@Override
protected void doRun() {
getTestProject().deleteModelElement(modelElement);
}
}.run(false);
List<AbstractOperation> operations = WorkspaceManager.getProjectSpace(testProject).getOperations();
if (operations.size() == 0) {
throw new IllegalStateException("No operations recorded");
}
final CreateDeleteOperation operation = (CreateDeleteOperation) operations.get(operations.size() - 1);
new EMFStoreCommand() {
@Override
protected void doRun() {
CreateDeleteOperation reverse = (CreateDeleteOperation) operation.reverse();
reverse.apply(getTestProject());
}
}.run(false);
}
/**
* Removes a referenced model element form a non-containment reference of a randomly selected ME.
*/
@SuppressWarnings("unchecked")
public void doNonContainmentReferenceRemove() {
EObject me = getRandomME(getTestProject());
while (me.eCrossReferences().size() < 1) {
me = getRandomME(getTestProject());
}
int indexToRemove = getRandomPosition(me.eCrossReferences().size());
EObject meToRemove = me.eCrossReferences().get(indexToRemove);
EReference refToChange = findReference(me, meToRemove);
Object object = me.eGet(refToChange);
if (refToChange.isMany()) {
EList<EObject> eList = (EList<EObject>) object;
eList.remove(meToRemove);
} else {
me.eSet(refToChange, null);
}
}
/**
* This finds an EReference on modelElement that matches referencedME.
*
* @param modelElement
* @param referencedME
* @return
*/
@SuppressWarnings("unchecked")
private EReference findReference(EObject modelElement, EObject referencedME) {
List<EReference> refsMatchingReferencedME = new ArrayList<EReference>();
for (EReference ref : modelElement.eClass().getEAllReferences()) {
if (!(ref.isContainer() || ref.isContainment())
&& (ref.getEReferenceType().equals(referencedME.eClass()) || ref.getEReferenceType().isSuperTypeOf(
referencedME.eClass())) || ref.getEReferenceType().equals(EcorePackage.eINSTANCE.getEObject())) {
refsMatchingReferencedME.add(ref);
}
}
if (refsMatchingReferencedME.size() == 1) {
return refsMatchingReferencedME.get(0);
}
for (EReference ref : refsMatchingReferencedME) {
Object object = modelElement.eGet(ref);
if (object == null) {
continue;
}
if (ref.isMany()) {
EList<EObject> eList = (EList<EObject>) object;
if (eList.contains(referencedME)) {
return ref;
}
} else {
if (modelElement.eGet(ref).equals(referencedME)) {
return ref;
}
}
}
return null;
}
/**
* Finds an attribute with isMany = true and moves elements inside this attribute.
*/
public void doMultiAttributeMove() {
EObject me = getRandomME(getTestProject());
EAttribute attributeToChange = getRandomAttribute(me);
int tries = 0;
// since isMany() attributes are very rare, we try for limited times to find one.
while (attributeToChange == null || !attributeToChange.isMany()) {
me = createRandomME();
attributeToChange = getRandomAttribute(me);
tries++;
if (tries > 2000) {
return;
}
}
moveMultiAttributeValue(me, attributeToChange);
}
}