/******************************************************************************
* Copyright (c) 2016 Oracle
* 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:
* Konstantin Komissarchik - initial implementation and ongoing maintenance
******************************************************************************/
package org.eclipse.sapphire.tests.modeling;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.sapphire.tests.SapphireTestCase;
import org.eclipse.sapphire.ui.util.TopologicalSorter;
import org.eclipse.sapphire.ui.util.TopologicalSorter.Entity;
import org.junit.Test;
/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
@SuppressWarnings( "unchecked" )
public final class TopologicalSorterTests extends SapphireTestCase
{
@Test
public void test_1()
{
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
b.before( a );
a.after( c );
testWithCycleBreaking( sorter, list( b, c, a ) );
}
@Test
public void test_2()
{
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
b.before( a );
a.after( c );
c.before( b );
testWithCycleBreaking( sorter, list( c, b, a ) );
}
@Test
public void test_3()
{
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
b.before( a );
a.after( c );
c.before( b );
testWithCycleBreaking( sorter, list( c, b, a ) );
}
@Test
public void test_4()
{
// One cycle in a graph that has a leaf.
//
// ----- D
// | ^
// v |
// A --> B --> C
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
final TopologicalSorter.Entity d = sorter.entity( "D" );
a.after( b );
b.after( c );
c.after( d );
d.after( b );
testWithCycleBreaking( sorter, list( d, c, b, a ), list( list( b, c, d ) ) );
}
@Test
public void test_5()
{
// Two cycles in a graph that has a leaf.
//
// ----- D ----- G
// | ^ | ^
// v | v |
// A --> B --> C --> E --> F --> H
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
final TopologicalSorter.Entity d = sorter.entity( "D" );
final TopologicalSorter.Entity e = sorter.entity( "E" );
final TopologicalSorter.Entity f = sorter.entity( "F" );
final TopologicalSorter.Entity g = sorter.entity( "G" );
final TopologicalSorter.Entity h = sorter.entity( "H" );
a.after( b );
b.after( c );
c.after( d );
d.after( b );
c.after( e );
e.after( f );
f.after( g );
g.after( e );
f.after( h );
testWithCycleBreaking( sorter, list( d, g, h, f, e, c, b, a ), list( list( b, c, d ), list( e, f, g ) ) );
}
@Test
public void test_6()
{
// One cycle in a graph with no leaves.
//
// ----- C
// | ^
// v |
// A --> B
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
a.after( b );
b.after( c );
c.after( a );
testWithCycleBreaking( sorter, list( c, b, a ), list( list( a, b, c ) ) );
}
@Test
public void test_7()
{
// Cycle with another cycle in a graph that has a leaf.
//
// G --> F
// ^ |
// | v
// I <-- H <-- E
// | ^
// v |
// A --> B --> C --> D
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
final TopologicalSorter.Entity d = sorter.entity( "D" );
final TopologicalSorter.Entity e = sorter.entity( "E" );
final TopologicalSorter.Entity f = sorter.entity( "F" );
final TopologicalSorter.Entity g = sorter.entity( "G" );
final TopologicalSorter.Entity h = sorter.entity( "H" );
final TopologicalSorter.Entity i = sorter.entity( "I" );
a.after( b );
b.after( c );
c.after( d );
d.after( e );
e.after( h );
h.after( i );
i.after( b );
h.after( g );
g.after( f );
f.after( e );
testWithCycleBreaking( sorter, list( i, f, g, h, e, d, c, b, a ), list( list( b, c, d, e, h, i ), list( e, h, g, f ) ) );
}
@Test
public void test_8()
{
// Cycle with another cycle in a graph that has no leaves.
//
// G --> F
// ^ |
// | v
// I <-- H <-- E
// | ^
// v |
// B --> C --> D
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
final TopologicalSorter.Entity d = sorter.entity( "D" );
final TopologicalSorter.Entity e = sorter.entity( "E" );
final TopologicalSorter.Entity f = sorter.entity( "F" );
final TopologicalSorter.Entity g = sorter.entity( "G" );
final TopologicalSorter.Entity h = sorter.entity( "H" );
final TopologicalSorter.Entity i = sorter.entity( "I" );
b.after( c );
c.after( d );
d.after( e );
e.after( h );
h.after( i );
i.after( b );
h.after( g );
g.after( f );
f.after( e );
testWithCycleBreaking( sorter, list( i, f, g, h, e, d, c, b ), list( list( b, c, d, e, h, i ), list( e, h, g, f ) ) );
}
@Test
public void test_9()
{
// Graph with a self cycle.
//
// -------
// | |
// v |
// A --> B -----
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity b = sorter.entity( "B" );
a.after( b );
b.after( b );
testWithCycleBreaking( sorter, list( b, a ), list( list( b ) ) );
}
@Test
public void test_10()
{
// Two detached sub-graphs. One of a sub-graphs has a cycle.
//
// ----- D
// | ^
// v |
// A --> B --> C
//
// E --> F --> G
// |
// v
// H
final TopologicalSorter<String> sorter = new TopologicalSorter<String>();
final TopologicalSorter.Entity a = sorter.entity( "A" );
final TopologicalSorter.Entity b = sorter.entity( "B" );
final TopologicalSorter.Entity c = sorter.entity( "C" );
final TopologicalSorter.Entity d = sorter.entity( "D" );
final TopologicalSorter.Entity e = sorter.entity( "E" );
final TopologicalSorter.Entity f = sorter.entity( "F" );
final TopologicalSorter.Entity g = sorter.entity( "G" );
final TopologicalSorter.Entity h = sorter.entity( "H" );
a.after( b );
b.after( c );
c.after( d );
d.after( b );
e.after( f );
f.after( g );
f.after( h );
testWithCycleBreaking( sorter, list( d, c, b, a, g, h, f, e ), list( list( b, c, d ) ) );
}
private static void testWithCycleBreaking( final TopologicalSorter<String> sorter,
final List<TopologicalSorter.Entity> expectedOrder )
{
testWithCycleBreaking( sorter, expectedOrder, Collections.<List<TopologicalSorter.Entity>>emptyList() );
}
private static void testWithCycleBreaking( final TopologicalSorter<String> sorter,
final List<TopologicalSorter.Entity> expectedOrder,
final List<List<TopologicalSorter.Entity>> expectedCycles )
{
final List<List<TopologicalSorter.Entity>> actualCycles = new ArrayList<List<TopologicalSorter.Entity>>();
final TopologicalSorter.CycleListener listener = new TopologicalSorter.CycleListener()
{
@Override
public void handleCycle( final List<Entity> cycle )
{
actualCycles.add( cycle );
}
};
sorter.addCycleListener( listener );
final List<String> actualOrder = sorter.sort();
final List<String> expectedOrderAsStrings = new ArrayList<String>();
for( TopologicalSorter.Entity entity : expectedOrder )
{
expectedOrderAsStrings.add( (String) entity.data() );
}
assertEquals( expectedOrderAsStrings, actualOrder );
assertEquals( expectedCycles, actualCycles );
}
}