/******************************************************************************* * Copyright (c) 2015, 2016 Obeo 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: * Obeo - initial API and implementation * Alexandra Buzila - bug 478620 * Martin Fleck - bug 507177 *******************************************************************************/ package org.eclipse.emf.compare.tests.utils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.util.Iterator; import java.util.Set; import org.eclipse.emf.compare.graph.IGraph; import org.eclipse.emf.compare.graph.PruningIterator; import org.eclipse.emf.compare.internal.utils.Graph; import org.junit.Test; /** * We will use this to test the utility methods exposed by the {@link Graph}. * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> */ @SuppressWarnings({"nls", "unchecked" }) public abstract class AbstractGraphTest<E> { protected abstract E toType(final String name); protected abstract IGraph<E> createGraph(); /** * Test correct subgraph. * * <pre> * _A_ * / | \ * B C D * | | * E F * </pre> */ @Test public void testBuildSubGraph() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("B"), toType("C"), toType("D"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("E"))); graph.addChildren(toType("D"), ImmutableSet.of(toType("F"))); Set<E> subgraph = graph.getSubgraphContaining(toType("D"), ImmutableSet.of(toType("C"))); assertEquals(4, subgraph.size()); assertTrue( subgraph.containsAll(Lists.newArrayList(toType("A"), toType("B"), toType("D"), toType("F")))); } /** * Test avoidance of infinite loops in cyclic graph. * * <pre> * C-+ * | | * B-+ * | * A * </pre> */ @Test public void testPrune() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("B"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("C"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("B"))); PruningIterator<E> iterator = graph.breadthFirstIterator(); while (iterator.hasNext()) { iterator.next(); iterator.prune(); } } /** * Tests breadth first iteration with the following Graph: * * <pre> * A I J * / \ / / \ * B C G K L * / / \ / \ / \ * D E F H M N * * We expect our iteration to go in the following order: * three first items, in unspecified order : A, I, J * next five, in unspecified order : B, C, G, K, L * finally, still in unspecified order : D, E, F, H, M, N * </pre> */ @Test public void testBreadthFirstIteration() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("B"), toType("C"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("D"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("E"), toType("F"))); graph.addChildren(toType("I"), ImmutableSet.of(toType("G"))); graph.addChildren(toType("G"), ImmutableSet.of(toType("F"), toType("H"))); graph.addChildren(toType("J"), ImmutableSet.of(toType("K"), toType("L"))); graph.addChildren(toType("L"), ImmutableSet.of(toType("M"), toType("N"))); Set<E> firstThree = Sets.newHashSet(toType("A"), toType("I"), toType("J")); Set<E> nextFive = Sets.newHashSet(toType("B"), toType("C"), toType("G"), toType("K"), toType("L")); Set<E> finalSix = Sets.newHashSet(toType("D"), toType("E"), toType("F"), toType("H"), toType("M"), toType("N")); PruningIterator<E> iterator = graph.breadthFirstIterator(); assertTrue(firstThree.remove(iterator.next())); assertTrue(firstThree.remove(iterator.next())); assertTrue(firstThree.remove(iterator.next())); assertTrue(firstThree.isEmpty()); assertTrue(nextFive.remove(iterator.next())); assertTrue(nextFive.remove(iterator.next())); assertTrue(nextFive.remove(iterator.next())); assertTrue(nextFive.remove(iterator.next())); assertTrue(nextFive.remove(iterator.next())); assertTrue(nextFive.isEmpty()); assertTrue(finalSix.remove(iterator.next())); assertTrue(finalSix.remove(iterator.next())); assertTrue(finalSix.remove(iterator.next())); assertTrue(finalSix.remove(iterator.next())); assertTrue(finalSix.remove(iterator.next())); assertTrue(finalSix.remove(iterator.next())); assertTrue(finalSix.isEmpty()); assertFalse(iterator.hasNext()); } /** * This test case ensures that pruned elements are not returned in a scenario where the next iterator in * the graph has already been prepared. This test uses the following graph: * * <pre> * A B * \ / * C * </pre> */ @Test public void testBreadthFirstIteration_MultipleParents() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("C"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("C"))); PruningIterator<E> breadthFirstIterator = graph.breadthFirstIterator(); assertTrue(breadthFirstIterator.hasNext()); assertEquals(toType("A"), breadthFirstIterator.next()); assertTrue(breadthFirstIterator.hasNext()); assertEquals(toType("B"), breadthFirstIterator.next()); breadthFirstIterator.prune(); // prune all children of B: [C] assertFalse(breadthFirstIterator.hasNext()); } /** * This test case ensures that pruned elements are not returned in a scenario where the next iterator in * the graph has already been prepared: * * <pre> * _A_ B * / | \ / * D E C * </pre> */ @Test public void testBreadthFirstIteration_MultipleParentsMultipleChildren() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("C"), toType("D"), toType("E"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("C"), toType("D"))); PruningIterator<E> breadthFirstIterator = graph.breadthFirstIterator(); assertTrue(breadthFirstIterator.hasNext()); assertEquals(toType("A"), breadthFirstIterator.next()); assertTrue(breadthFirstIterator.hasNext()); assertEquals(toType("B"), breadthFirstIterator.next()); breadthFirstIterator.prune(); // prune all children of B: [C, D] assertTrue(breadthFirstIterator.hasNext()); assertEquals(toType("E"), breadthFirstIterator.next()); assertFalse(breadthFirstIterator.hasNext()); } /** * This test case ensures that elements over multiple lvels are pruned correctly and are not returned * through another path in the graph: * * <pre> * A B * / \ / * D C * \ / * E * </pre> */ @Test public void testBreadthFirstIteration_PruneMultipleLevels() { IGraph<E> graph = new Graph<E>(); // first level graph.addChildren(toType("A"), ImmutableSet.of(toType("D"), toType("C"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("C"))); // second level graph.addChildren(toType("D"), ImmutableSet.of(toType("E"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("E"))); PruningIterator<E> breadthFirstIterator = graph.breadthFirstIterator(); // iterate over first level assertTrue(breadthFirstIterator.hasNext()); assertEquals(toType("A"), breadthFirstIterator.next()); assertTrue(breadthFirstIterator.hasNext()); assertEquals(toType("B"), breadthFirstIterator.next()); // prune all children of B: [C and also E from the level below] breadthFirstIterator.prune(); assertTrue(breadthFirstIterator.hasNext()); assertEquals(toType("D"), breadthFirstIterator.next()); // no more children as C and E have been pruned assertFalse(breadthFirstIterator.hasNext()); } @Test public void testTreeIteration_1() { IGraph<E> graph = getAcyclicGraph(); Iterator<E> iteratorOnA = graph.depthFirstIterator(toType("A")); assertEquals(toType("A"), iteratorOnA.next()); assertEquals(toType("B"), iteratorOnA.next()); assertEquals(toType("D"), iteratorOnA.next()); assertEquals(toType("C"), iteratorOnA.next()); assertEquals(toType("E"), iteratorOnA.next()); assertEquals(toType("F"), iteratorOnA.next()); assertFalse(iteratorOnA.hasNext()); } @Test public void testTreeIteration_2() { IGraph<E> graph = getAcyclicGraph(); Iterator<E> iteratorOnC = graph.depthFirstIterator(toType("C")); assertEquals(toType("C"), iteratorOnC.next()); assertEquals(toType("E"), iteratorOnC.next()); assertEquals(toType("F"), iteratorOnC.next()); assertFalse(iteratorOnC.hasNext()); } @Test public void testTreeIteration_3() { IGraph<E> graph = getAcyclicGraph(); Iterator<E> iteratorOnI = graph.depthFirstIterator(toType("I")); assertEquals(toType("I"), iteratorOnI.next()); assertEquals(toType("G"), iteratorOnI.next()); assertEquals(toType("F"), iteratorOnI.next()); assertEquals(toType("H"), iteratorOnI.next()); assertFalse(iteratorOnI.hasNext()); } @Test public void testTreeIteration_4() { IGraph<E> graph = getAcyclicGraph(); Iterator<E> iteratorOnJ = graph.depthFirstIterator(toType("J")); assertEquals(toType("J"), iteratorOnJ.next()); assertEquals(toType("K"), iteratorOnJ.next()); assertEquals(toType("L"), iteratorOnJ.next()); assertEquals(toType("M"), iteratorOnJ.next()); assertEquals(toType("N"), iteratorOnJ.next()); assertFalse(iteratorOnJ.hasNext()); } @Test public void testDepthIterationWithCycles_1() { IGraph<E> graph = getGraphWithCycles(); Iterator<E> iteratorOnA = graph.depthFirstIterator(toType("A")); assertEquals(toType("A"), iteratorOnA.next()); assertEquals(toType("B"), iteratorOnA.next()); assertEquals(toType("D"), iteratorOnA.next()); assertEquals(toType("E"), iteratorOnA.next()); assertEquals(toType("C"), iteratorOnA.next()); assertEquals(toType("F"), iteratorOnA.next()); assertEquals(toType("H"), iteratorOnA.next()); assertFalse(iteratorOnA.hasNext()); } @Test public void testDepthIterationWithCycles_2() { IGraph<E> graph = getGraphWithCycles(); Iterator<E> iteratorOnC = graph.depthFirstIterator(toType("C")); assertEquals(toType("C"), iteratorOnC.next()); assertEquals(toType("A"), iteratorOnC.next()); assertEquals(toType("B"), iteratorOnC.next()); assertEquals(toType("D"), iteratorOnC.next()); assertEquals(toType("E"), iteratorOnC.next()); assertEquals(toType("F"), iteratorOnC.next()); assertEquals(toType("H"), iteratorOnC.next()); assertFalse(iteratorOnC.hasNext()); } @Test public void testDepthIterationWithCycles_3() { IGraph<E> graph = getGraphWithCycles(); Iterator<E> iteratorOnI = graph.depthFirstIterator(toType("I")); assertEquals(toType("I"), iteratorOnI.next()); assertEquals(toType("G"), iteratorOnI.next()); assertEquals(toType("F"), iteratorOnI.next()); assertEquals(toType("H"), iteratorOnI.next()); assertFalse(iteratorOnI.hasNext()); } @Test public void testDepthIterationWithCycles_4() { IGraph<E> graph = getGraphWithCycles(); Iterator<E> iteratorOnJ = graph.depthFirstIterator(toType("J")); assertEquals(toType("J"), iteratorOnJ.next()); assertEquals(toType("K"), iteratorOnJ.next()); assertEquals(toType("M"), iteratorOnJ.next()); assertEquals(toType("L"), iteratorOnJ.next()); assertEquals(toType("N"), iteratorOnJ.next()); assertFalse(iteratorOnJ.hasNext()); } /** * Test the BreadthFirstIterator with the following cyclic graph: * * <pre> * A * / \ * B = C * </pre> */ @Test public void testBug503035_1() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("B"), toType("C"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("C"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("B"))); Iterator<E> it = graph.breadthFirstIterator(); assertTrue(it.hasNext()); assertEquals(toType("A"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("B"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("C"), it.next()); assertFalse(it.hasNext()); } /** * Test the BreadthFirstIterator with the following cyclic graph: * * <pre> * A * / \ * B = C * / * D * </pre> */ @Test public void testBug503035_2() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("B"), toType("C"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("D"), toType("C"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("B"))); Iterator<E> it = graph.breadthFirstIterator(); assertTrue(it.hasNext()); assertEquals(toType("A"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("B"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("C"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("D"), it.next()); assertFalse(it.hasNext()); } /** * Test the BreadthFirstIterator with the following cyclic graph: * * <pre> * A B * | | * C = D * </pre> */ @Test public void testBug503035_3() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("C"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("D"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("D"))); graph.addChildren(toType("D"), ImmutableSet.of(toType("C"))); Iterator<E> it = graph.breadthFirstIterator(); assertTrue(it.hasNext()); assertEquals(toType("A"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("B"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("C"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("D"), it.next()); assertFalse(it.hasNext()); } /** * Test the BreadthFirstIterator with the following cyclic graph: * * <pre> * A * /|\ * / | \ * B C D * \\===// * </pre> */ @Test public void testBug503035_4() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("B"), toType("C"), toType("D"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("D"))); graph.addChildren(toType("D"), ImmutableSet.of(toType("B"))); Iterator<E> it = graph.breadthFirstIterator(); assertTrue(it.hasNext()); assertEquals(toType("A"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("B"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("C"), it.next()); assertTrue(it.hasNext()); assertEquals(toType("D"), it.next()); assertFalse(it.hasNext()); } /** * @return The following acyclic graph: * * <pre> * A I J * / \ / / \ * B C G K L * / / \ / \ / \ * D E F H M N * </pre> */ protected IGraph<E> getAcyclicGraph() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("B"), toType("C"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("D"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("E"), toType("F"))); graph.addChildren(toType("I"), ImmutableSet.of(toType("G"))); graph.addChildren(toType("G"), ImmutableSet.of(toType("F"), toType("H"))); graph.addChildren(toType("J"), ImmutableSet.of(toType("K"), toType("L"))); graph.addChildren(toType("L"), ImmutableSet.of(toType("M"), toType("N"))); return graph; } /** * @return The following cyclic graph: * * <pre> * A I J * / \\ / / \ * B C G K L * / / \ / \ \ // \ * D - E F = H M N * </pre> */ protected IGraph<E> getGraphWithCycles() { IGraph<E> graph = createGraph(); graph.addChildren(toType("A"), ImmutableSet.of(toType("B"), toType("C"))); graph.addChildren(toType("B"), ImmutableSet.of(toType("D"))); graph.addChildren(toType("D"), ImmutableSet.of(toType("E"))); graph.addChildren(toType("C"), ImmutableSet.of(toType("A"), toType("E"), toType("F"))); graph.addChildren(toType("I"), ImmutableSet.of(toType("G"))); graph.addChildren(toType("G"), ImmutableSet.of(toType("F"), toType("H"))); graph.addChildren(toType("J"), ImmutableSet.of(toType("K"), toType("L"))); graph.addChildren(toType("K"), ImmutableSet.of(toType("M"))); graph.addChildren(toType("M"), ImmutableSet.of(toType("L"))); graph.addChildren(toType("L"), ImmutableSet.of(toType("M"), toType("N"))); graph.addChildren(toType("F"), ImmutableSet.of(toType("H"))); graph.addChildren(toType("H"), ImmutableSet.of(toType("F"))); return graph; } }