/******************************************************************************* * Copyright (c) 2012, 2014 Obeo. * 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: * Obeo - initial API and implementation *******************************************************************************/ package org.eclipse.emf.compare.tests.framework; import static com.google.common.base.Predicates.and; import static org.eclipse.emf.compare.utils.EMFComparePredicates.added; import static org.eclipse.emf.compare.utils.EMFComparePredicates.addedToReference; import static org.eclipse.emf.compare.utils.EMFComparePredicates.changedAttribute; import static org.eclipse.emf.compare.utils.EMFComparePredicates.changedReference; import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide; import static org.eclipse.emf.compare.utils.EMFComparePredicates.removed; import static org.eclipse.emf.compare.utils.EMFComparePredicates.removedFromReference; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import java.lang.reflect.Field; import java.util.Iterator; import java.util.List; import org.eclipse.emf.compare.Comparison; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.DifferenceSource; import org.eclipse.emf.compare.Match; import org.eclipse.emf.compare.scope.FilterComparisonScope; import org.eclipse.emf.compare.scope.IComparisonScope; import org.eclipse.emf.compare.utils.EMFComparePredicates; import org.eclipse.emf.ecore.EObject; /** * Provides specific assertions for EMF Compare tests. * * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ @SuppressWarnings("nls") public class EMFCompareAssert { /** * This can be used to check whether all objects of the given list have a corresponding {@link Match} in * the given {@link Comparison}. If one of said EObjects is not matched, we will check whether it is * included in the given <code>scope</code> if it is a {@link FilterComparisonScope}. * * @param eObjects * The list of EObjects for which we need corresponding {@link Match}es. * @param comparison * The {@link Comparison} in which we are to check for Matches. * @param scope * The scope that has been used to create the given <code>comparison</code>. */ public static void assertAllMatched(List<EObject> eObjects, Comparison comparison, IComparisonScope scope) { final Predicate<? super EObject> scopeFilter; if (scope instanceof FilterComparisonScope) { scopeFilter = getResourceChildrenFilteringPredicate((FilterComparisonScope)scope); } else { scopeFilter = Predicates.alwaysTrue(); } final Iterator<EObject> eObjectIterator = eObjects.iterator(); while (eObjectIterator.hasNext()) { final EObject eObject = eObjectIterator.next(); final Match match = comparison.getMatch(eObject); assertTrue(eObject + " has no match", match != null || !scopeFilter.apply(eObject)); } } /** * Asserts that the given list of differences contains a ReferenceChange describing the given change for a * single-valued reference. The {@code differences} list will be updated by this call, removing the * corresponding Diff if it could be located. * <p> * Note that in order for this to work, we expect the EObjects to have a "name" feature returning a String * so that we can compare it to the given qualified names. * </p> * * @param differences * List of differences in which to seek for a particular reference change. * @param qualifiedName * Qualified name of the EObject which reference we expect to have changed. * @param referenceName * Name of the reference which values we expect to have changed. <b>Note</b> that we expect * this reference to be single-valued. * @param fromQualifiedName * Qualified name of the original value of this reference (can be either the origin or right * value). * @param toQualifiedName * Qualified name of the value to which this reference has been changed (can be either left or * right in three-way comparisons, only left for two-way). * @param side * The side from which we expect this diff to originate. * @see EMFComparePredicates#changedReference(String, String, String, String) */ public static void assertChangedReference(List<Diff> differences, String qualifiedName, String referenceName, String fromQualifiedName, String toQualifiedName, DifferenceSource side) { final Predicate<? super Diff> changedReferenceOnSide = and(fromSide(side), changedReference(qualifiedName, referenceName, fromQualifiedName, toQualifiedName)); final Diff matchingDiff = removeFirst(differences.iterator(), changedReferenceOnSide); assertNotNull(matchingDiff); } /** * Asserts that the given list of differences contains a ReferenceChange describing the removal of a value * from a multi-valued reference. The {@code differences} list will be updated by this call, removing the * corresponding Diff if it could be located. * <p> * Note that in order for this to work, we expect the EObjects to have a "name" feature returning a String * so that we can compare it to the given qualified names. * </p> * * @param differences * List of differences in which to seek for a particular reference change. * @param qualifiedName * Qualified name of the EObject which reference we expect to have changed. * @param referenceName * Name of the reference which values we expect to have changed. <b>Note</b> that we expect * this reference to be multi-valued. * @param removedValueQualifiedName * Qualified name of the value we expect to have been removed from that reference's list of * values. * @param side * The side from which we expect this diff to originate. * @see EMFComparePredicates#removedFromReference(String, String, String) */ public static void assertRemovedFromReference(List<Diff> differences, String qualifiedName, String referenceName, String removedValueQualifiedName, DifferenceSource side) { final Predicate<? super Diff> removedFromReferenceOnSide = and(fromSide(side), removedFromReference(qualifiedName, referenceName, removedValueQualifiedName)); final Diff matchingDiff = removeFirst(differences.iterator(), removedFromReferenceOnSide); assertNotNull(matchingDiff); } /** * Asserts that the given list of differences contains a ReferenceChange describing the addition of a * value into a multi-valued reference. The {@code differences} list will be updated by this call, * removing the corresponding Diff if it could be located. * <p> * Note that in order for this to work, we expect the EObjects to have a "name" feature returning a String * so that we can compare it to the given qualified names. * </p> * * @param differences * List of differences in which to seek for a particular reference change. * @param qualifiedName * Qualified name of the EObject which reference we expect to have changed. * @param referenceName * Name of the reference which values we expect to have changed. <b>Note</b> that we expect * this reference to be multi-valued. * @param addedValueQualifiedName * Qualified name of the value we expect to have been added to that reference's list of values. * @param side * The side from which we expect this diff to originate. * @see EMFComparePredicates#addedToReference(String, String, String) */ public static void assertAddedToReference(List<Diff> differences, String qualifiedName, String referenceName, String addedValueQualifiedName, DifferenceSource side) { final Predicate<? super Diff> addedToReferenceOnSide = and(fromSide(side), addedToReference(qualifiedName, referenceName, addedValueQualifiedName)); final Diff matchingDiff = removeFirst(differences.iterator(), addedToReferenceOnSide); assertNotNull(matchingDiff); } /** * Asserts that the given list of differences contains an AttributeChange describing the given change for * a single-valued attribute. The {@code differences} list will be updated by this call, removing the * corresponding Diff if it could be located. * <p> * Note that in order for this to work, we expect the EObjects to have a "name" feature returning a String * so that we can compare it to the given qualified names. * </p> * * @param differences * List of differences in which to seek for a particular attribute change. * @param qualifiedName * Qualified name of the EObject which attribute we expect to have changed. * @param attributeName * Name of the attribute which values we expect to have changed. <b>Note</b> that we expect * this attribute to be single-valued. * @param fromValue * The original value of this attribute. Can be either the origin or right value. * @param toValue * The value to which this attribute has been changed. Can be either left or right for a * three-way comparison, only left in two-way. * @param side * The side from which we expect this diff to originate. * @see EMFComparePredicates#changedAttribute(String, String, Object, Object) */ public static void assertChangedAttribute(List<Diff> differences, String qualifiedName, String attributeName, Object fromValue, Object toValue, DifferenceSource side) { final Predicate<? super Diff> changedAttributeOnSide = and(fromSide(side), changedAttribute(qualifiedName, attributeName, fromValue, toValue)); final Diff matchingDiff = removeFirst(differences.iterator(), changedAttributeOnSide); assertNotNull(matchingDiff); } /** * Asserts that the given list of differences contains a ReferenceChange describing the given element * addition. This is only meant for containment changes. The {@code differences} list will be updated by * this call, removing the corresponding Diff if it could be located. * <p> * Note that in order for this to work, we expect the EObjects to have a "name" feature returning a String * so that we can compare it to the given qualified names. * </p> * * @param differences * List of differences in which to seek for a particular element addition. * @param qualifiedName * Qualified name of the EObject we expect to have been added. * @param side * The side from which we expect this diff to originate. * @see EMFComparePredicates#added(String) */ public static void assertAdded(List<Diff> differences, String qualifiedName, DifferenceSource side) { final Predicate<? super Diff> addedOnSide = and(fromSide(side), added(qualifiedName)); final Diff matchingDiff = removeFirst(differences.iterator(), addedOnSide); assertNotNull(matchingDiff); } /** * Asserts that the given list of differences contains a ReferenceChange describing the given element * deletion. This is only meant for containment changes. The {@code differences} list will be updated by * this call, removing the corresponding Diff if it could be located. * <p> * Note that in order for this to work, we expect the EObjects to have a "name" feature returning a String * so that we can compare it to the given qualified names. * </p> * * @param differences * List of differences in which to seek for a particular element deletion. * @param qualifiedName * Qualified name of the EObject we expect to have been deleted. * @param side * The side from which we expect this diff to originate. * @see EMFComparePredicates#removed(String) */ public static void assertRemoved(List<Diff> differences, String qualifiedName, DifferenceSource side) { final Predicate<? super Diff> removedOnSide = and(fromSide(side), removed(qualifiedName)); final Diff matchingDiff = removeFirst(differences.iterator(), removedOnSide); assertNotNull(matchingDiff); } /** * Retrieves the Predicate that is used by the given scope in order to filter out Resource children. * <p> * This uses reflection to access a protected field, and is only meant for testing purposes. * </p> * * @param scope * The scope which predicate we need to retrieve. * @return The predicate that was used by the given scope to filter out Resource children. */ @SuppressWarnings("unchecked") private static Predicate<? super EObject> getResourceChildrenFilteringPredicate( FilterComparisonScope scope) { final String fieldName = "resourceContentFilter"; //$NON-NLS-1$ try { final Field field = FilterComparisonScope.class.getDeclaredField(fieldName); field.setAccessible(true); return (Predicate<? super EObject>)field.get(scope); } catch (Exception e) { fail("Could not retrieve the filtering predicate of " + scope.getClass().getName()); //$NON-NLS-1$ } // Unreachable code return null; } /** * Removes and returns the first element returned by {@code iterator} that satisfies the given predicate. * * @param iterator * Iterators over which elements we are to iterate. * @param predicate * The predicate which needs to be satisified. * @return The first element of {@code iterator} that satisfies {@code predicate}, {@code null} if none. */ private static <T> T removeFirst(Iterator<T> iterator, Predicate<? super T> predicate) { while (iterator.hasNext()) { T element = iterator.next(); if (predicate.apply(element)) { iterator.remove(); return element; } } return null; } }