/******************************************************************************* * Copyright (c) 2010 Technical University of Denmark. * 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: * Patrick Koenemann, DTU Informatics - initial API and implementation *******************************************************************************/ package org.eclipse.emf.compare.mpatch.test.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Collection; import java.util.Collections; import java.util.List; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.compare.diff.metamodel.ComparisonResourceSnapshot; import org.eclipse.emf.compare.diff.metamodel.ComparisonSnapshot; import org.eclipse.emf.compare.diff.metamodel.DiffElement; import org.eclipse.emf.compare.mpatch.ChangeGroup; import org.eclipse.emf.compare.mpatch.IndepChange; import org.eclipse.emf.compare.mpatch.MPatchModel; import org.eclipse.emf.compare.mpatch.UnknownChange; import org.eclipse.emf.compare.mpatch.apply.generic.util.InternalReferencesTransformation; import org.eclipse.emf.compare.mpatch.apply.generic.util.MPatchDependencyTransformation; import org.eclipse.emf.compare.mpatch.apply.util.MPatchResolver; import org.eclipse.emf.compare.mpatch.apply.util.MPatchValidator; import org.eclipse.emf.compare.mpatch.common.util.CommonUtils; import org.eclipse.emf.compare.mpatch.emfdiff2mpatch.util.TransformationLauncher; import org.eclipse.emf.compare.mpatch.extension.IModelDescriptorCreator; import org.eclipse.emf.compare.mpatch.extension.ISymbolicReferenceCreator; import org.eclipse.emf.compare.mpatch.extension.MPatchApplicationResult; import org.eclipse.emf.compare.mpatch.extension.ResolvedSymbolicReferences; import org.eclipse.emf.compare.mpatch.extension.MPatchApplicationResult.ApplicationStatus; import org.eclipse.emf.compare.mpatch.extension.ResolvedSymbolicReferences.ValidationResult; import org.eclipse.emf.compare.mpatch.symrefs.OclCondition; import org.eclipse.emf.compare.mpatch.symrefs.SymrefsPackage; import org.eclipse.emf.compare.mpatch.symrefs.util.OCLConditionHelper; import org.eclipse.emf.compare.mpatch.transform.util.GroupingTransformation; import org.eclipse.emf.compare.mpatch.util.ExtEcoreUtils; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceImpl; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.Diagnostician; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.query.conditions.eobjects.EObjectCondition; /** * Common test operations (test logic) for multiple test cases. * * @author Patrick Koenemann (pk@imm.dtu.dk) * */ public class CommonTestOperations { /** * This performs the following test cycle for a given model: *
null
. No other checks are performed.
*
* @param unchanged
* The URI of the unchanged version of a model.
* @param changed
* The URI of the changed version of a model.
* @param symrefCreator
* The symbolic reference creator used for this transformation.
* @param descriptorCreator
* The model descriptor creator used for this transformation.
* @param info
* Additional information to print in assert messages.
* @return The calculated MPatch.
*/
public static MPatchModel createMPatch(String unchanged, String changed, ISymbolicReferenceCreator symrefCreator,
IModelDescriptorCreator descriptorCreator, String info) {
// prepare models
final MPatchModel mpatch = CompareTestHelper.getMPatchFromUris(changed, unchanged, symrefCreator,
descriptorCreator);
assertNotNull("Preceeding transformation failed! Make sure mpatch can be produced for: " + unchanged + " and "
+ changed + " and " + info, mpatch);
return mpatch;
}
/**
* Check the application of differences by comparing the result with a given model, usually the changed version from
* which the mpatch was created from. There should be no differences between these two - if there are, then the test
* fails and the differences are given in the assert log message.
*
* (So this is the self-checking part of the test case, to make Fowler happy :o) [Refactoring, Fowler])
*
* @param appliedModel
* A model to which differences have been applied.
* @param originalChangedModel
* The changed version of the model from which the differences have been created.
*/
public static void checkAppliedModel(EObject appliedModel, EObject originalChangedModel, String info) {
// compare with target model and check whether they differ
final ComparisonSnapshot differences = CommonUtils.createEmfdiff(appliedModel, originalChangedModel, false);
final Collectionnull
. The current system time is put into a field in the array:
* null
.
* @param info
* Show additional information about the test case in the assert message, e.g. the use of particular
* creators for the transformation.
* @return A review of all applied changes.
*/
public static ComparisonSnapshot createAndApplyMPatch(MPatchModel mpatch, EObject model, EObject expectedModel,
PerformanceTimes times, String info) {
if (times != null)
times.tick(); // start timing
doTransformations(mpatch, times, info);
final ResolvedSymbolicReferences resolved = resolveAndValidate(mpatch, model, times, info);
final EObject newModel = applyMPatchToModel(mpatch, model, resolved, times, info);
final ComparisonResourceSnapshot emfdiff = compareChangedAndUnchangedModels(model, newModel, times, info);
checkAppliedModel(model, expectedModel, info);
checkBinding(resolved.getMPatchModelBinding(), info);
if (times != null)
times.setCheck(); // final check
return emfdiff;
}
/**
* Validate an eObject using the EMF Validation Framework ({@link Diagnostician}).
*
* @param eObject
* An eobject to validate.
* @param info
* Some addition information for the assert messages.
*/
public static void checkBinding(EObject eObject, String info) {
// validate the given element using the emf validation framework!
final Diagnostic diagnostic = Diagnostician.INSTANCE.validate(eObject);
assertNull("Validation was not successful! (" + info + "): " + eObject, diagnostic.getException());
assertEquals("Validation was not successful! (" + info + "): " + eObject + "\n" + getMessage(diagnostic),
Diagnostic.OK, diagnostic.getSeverity());
}
/**
* Compare the two given models and return their differences as an emfdiff.
*
* @param changedModel
* The changed version of a model.
* @param unchangedModel
* The unchanged version of the model.
* @param times
* For performance check (optional, can be null
).
* @param info
* Additional information for assert Strings.
* @return An emfdiff containing all differences between these two model versions.
*/
public static ComparisonResourceSnapshot compareChangedAndUnchangedModels(EObject changedModel,
EObject unchangedModel, PerformanceTimes times, String info) {
// run emf compare again to return review all applied differences
final ComparisonResourceSnapshot emfdiff = CommonUtils.createEmfdiff(changedModel, unchangedModel, false);
if (times != null)
times.setChanges(); // final comparison
// check the modified source model
final EObject appliedModel = CommonUtils.getModelFromEmfdiff(emfdiff, true); // get left model
assertNotNull("Model with applied diff must not be null! (" + info + ")", appliedModel);
assertEquals("The model to which we applied the diff must be our original model! (" + info + ")", changedModel,
appliedModel);
return emfdiff;
}
/**
* Create a copy of the given model and apply the MPatch to it. Then, check whether the application was successful
* by check the {@link MPatchApplicationResult}.
*
* @param mpatch
* The MPatch to apply.
* @param model
* The model to which the MPatch should be applied.
* @param resolved
* The resolution of symbolic references.
* @param times
* For performance check (optional, can be null
).
* @param info
* Additional information for assert Strings.
* @return A copy of original model
. Note that the MPatch was applied to the parameter
* model
!
*/
public static EObject applyMPatchToModel(MPatchModel mpatch, EObject model, ResolvedSymbolicReferences resolved,
PerformanceTimes times, String info) {
// create a new target model - a copy as the 'right' model, whereas the original is the 'left' model
final EObject newModel = EcoreUtil.copy(model);
final ResourceImpl newModelResource = new ResourceImpl();
newModelResource.getContents().add(newModel);
newModelResource.setURI(model.eResource().getURI());
if (times != null)
times.setCopy(); // model copy time
// apply differences *with* calculating the binding!
final MPatchApplicationResult result = TestConstants.DIFF_APPLIER.applyMPatch(resolved, true);
if (times != null)
times.setApply(); // diff application time
// since we apply the diff to the unchanged model, we require all changes to be applied successfully!
assertTrue("There are changes which failed (" + info + "): " + result.failed, result.failed.isEmpty());
assertTrue("There are changes with unsufficient cross-reference restore (" + info + "): "
+ result.crossReferences, result.crossReferences.isEmpty());
assertEquals("Diff application result is not successful (" + info + ")!", ApplicationStatus.SUCCESSFUL,
result.status);
return newModel;
}
/**
* Resolve the given MPatch to the given model and validate the resolution using the {@link MPatchValidator}.
*
* @param mpatch
* The MPatch to resolve.
* @param model
* The model to which the MPatch should be resolved.
* @param times
* For performance check (optional, can be null
).
* @param info
* Additional information for assert Strings.
* @return The resolution.
*/
public static ResolvedSymbolicReferences resolveAndValidate(MPatchModel mpatch, EObject model,
PerformanceTimes times, String info) {
// resolve symbolic references
final ResolvedSymbolicReferences resolved = MPatchResolver.resolveSymbolicReferences(mpatch, model,
ResolvedSymbolicReferences.RESOLVE_UNCHANGED);
if (times != null)
times.setResolve(); // resolution time
// validate all resolved references
final Listnull
).
* @param info
* Additional information for assert Strings.
*/
public static void doTransformations(MPatchModel mpatch, PerformanceTimes times, String info) {
// restructure differences, add dependencies, and replace internal references
try {
final int groups = GroupingTransformation.group(mpatch);
if (times != null)
times.setGroups(); // grouping time
assertTrue("We need at least one group after restructuring!", groups > 0);
MPatchDependencyTransformation.calculateDependencies(mpatch);
if (times != null)
times.setDeps(); // dependency calculation time
InternalReferencesTransformation.createInternalReferences(mpatch);
if (times != null)
times.setRefs(); // internal reference creation time
} catch (Exception e) {
e.printStackTrace();
fail("Grouping or dependency calculation failed (" + info + "): " + e.getMessage());
}
}
private static String getMessage(Diagnostic diagnostic) {
String msg = diagnostic.getMessage();
for (Diagnostic d : diagnostic.getChildren()) {
msg += "\n" + d.getMessage();
}
return msg;
}
/**
* Check whether outModel
contains an mpatch and that is has count
(ungrouped) changes in
* total.
*
* @param mpatch
* It is supposed to be the result of {@link TransformationLauncher#transform(List, StringBuffer)}.
* @param count
* The expected number of changed which is contained in the result.
*/
public static void checkForDifferences(MPatchModel mpatch, int count) {
// ignore unknown changes and groups here!
int counter = 0;
for (IndepChange change : mpatch.getChanges()) {
if (!(change instanceof UnknownChange || change instanceof ChangeGroup)) {
counter++;
}
}
assertEquals("MPatch does not contain " + count + " elements as expected!", count, counter);
}
/**
* This first performs an initial resolution of symbolic references in the given diff
. Then, the
* MPatchValidator is used to check whether all symbolic references resolved correctly and whether the state before
* the changes can be found in the given model.
*
* @param mpatch
* MPatch.
* @param model
* A model for which the reference resolution should be performed.
*/
public static void validateSymbolicReferenceResolution(MPatchModel mpatch, EObject model) {
try {
// resolve symbolic references
ResolvedSymbolicReferences mapping = MPatchResolver.resolveSymbolicReferences(mpatch, model,
ResolvedSymbolicReferences.RESOLVE_UNCHANGED);
assertNotNull("Result of symbolic reference resolution must not be null!", mapping);
// validate resolution
final List