//
//  ========================================================================
//  Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.util;

import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.*;

import java.util.Arrays;

import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;

public class TopologicalSortTest
{

    @Test
    public void testNoDependencies()
    {
        String[] s = { "D","E","C","B","A" };
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.sort(s);
        
        Assert.assertThat(s,Matchers.arrayContaining("D","E","C","B","A"));        
    }
    
    @Test
    public void testSimpleLinear()
    {
        String[] s = { "D","E","C","B","A" };
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.addDependency("B","A");
        ts.addDependency("C","B");
        ts.addDependency("D","C");
        ts.addDependency("E","D");
        
        ts.sort(s);
        
        Assert.assertThat(s,Matchers.arrayContaining("A","B","C","D","E"));        
    }

    @Test
    public void testDisjoint()
    {
        String[] s = { "A","C","B","CC","AA","BB"};
        
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.addDependency("B","A");
        ts.addDependency("C","B");
        ts.addDependency("BB","AA");
        ts.addDependency("CC","BB");
        
        ts.sort(s);
        
        Assert.assertThat(s,Matchers.arrayContaining("A","B","C","AA","BB","CC"));   
    }

    @Test
    public void testDisjointReversed()
    {
        String[] s = { "CC","AA","BB","A","C","B"};
        
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.addDependency("B","A");
        ts.addDependency("C","B");
        ts.addDependency("BB","AA");
        ts.addDependency("CC","BB");
        
        ts.sort(s);
          
        Assert.assertThat(s,Matchers.arrayContaining("AA","BB","CC","A","B","C"));  
    }

    @Test
    public void testDisjointMixed()
    {
        String[] s = { "CC","A","AA","C","BB","B"};
        
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.addDependency("B","A");
        ts.addDependency("C","B");
        ts.addDependency("BB","AA");
        ts.addDependency("CC","BB");
        
        ts.sort(s);

        // Check direct ordering
        Assert.assertThat(indexOf(s,"A"),lessThan(indexOf(s,"B"))); 
        Assert.assertThat(indexOf(s,"B"),lessThan(indexOf(s,"C"))); 
        Assert.assertThat(indexOf(s,"AA"),lessThan(indexOf(s,"BB"))); 
        Assert.assertThat(indexOf(s,"BB"),lessThan(indexOf(s,"CC"))); 
    }

    @Test
    public void testTree()
    {
        String[] s = { "LeafA0","LeafB0","LeafA1","Root","BranchA","LeafB1","BranchB"};
        
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.addDependency("BranchB","Root");
        ts.addDependency("BranchA","Root");
        ts.addDependency("LeafA1","BranchA");
        ts.addDependency("LeafA0","BranchA");
        ts.addDependency("LeafB0","BranchB");
        ts.addDependency("LeafB1","BranchB");
        
        ts.sort(s);
        
        // Check direct ordering
        Assert.assertThat(indexOf(s,"Root"),lessThan(indexOf(s,"BranchA")));  
        Assert.assertThat(indexOf(s,"Root"),lessThan(indexOf(s,"BranchB")));   
        Assert.assertThat(indexOf(s,"BranchA"),lessThan(indexOf(s,"LeafA0")));    
        Assert.assertThat(indexOf(s,"BranchA"),lessThan(indexOf(s,"LeafA1"))); 
        Assert.assertThat(indexOf(s,"BranchB"),lessThan(indexOf(s,"LeafB0")));    
        Assert.assertThat(indexOf(s,"BranchB"),lessThan(indexOf(s,"LeafB1")));   
        
        // check remnant ordering of original list
        Assert.assertThat(indexOf(s,"BranchA"),lessThan(indexOf(s,"BranchB")));  
        Assert.assertThat(indexOf(s,"LeafA0"),lessThan(indexOf(s,"LeafA1")));  
        Assert.assertThat(indexOf(s,"LeafB0"),lessThan(indexOf(s,"LeafB1")));  
    }

    @Test
    public void testPreserveOrder()
    {
        String[] s = { "Deep","Foobar","Wibble","Bozo","XXX","12345","Test"};
        
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.addDependency("Deep","Test");
        ts.addDependency("Deep","Wibble");
        ts.addDependency("Deep","12345");
        ts.addDependency("Deep","XXX");
        ts.addDependency("Deep","Foobar");
        ts.addDependency("Deep","Bozo");
        
        ts.sort(s);
        Assert.assertThat(s,Matchers.arrayContaining("Foobar","Wibble","Bozo","XXX","12345","Test","Deep")); 
    }
    
    @Test
    public void testSimpleLoop()
    {
        String[] s = { "A","B","C","D","E" };
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.addDependency("B","A");
        ts.addDependency("A","B");
        
        try
        {
            ts.sort(s);
            Assert.fail();
        }
        catch(IllegalStateException e)
        {
            Assert.assertThat(e.getMessage(),Matchers.containsString("cyclic"));
        }
    }

    @Test
    public void testDeepLoop()
    {
        String[] s = { "A","B","C","D","E" };
        TopologicalSort<String> ts = new TopologicalSort<>();
        ts.addDependency("B","A");
        ts.addDependency("C","B");
        ts.addDependency("D","C");
        ts.addDependency("E","D");
        ts.addDependency("A","E");
        try
        {
            ts.sort(s);
            Assert.fail();
        }
        catch(IllegalStateException e)
        {
            Assert.assertThat(e.getMessage(),Matchers.containsString("cyclic"));
        }
    }
    
    private int indexOf(String[] list,String s)
    {
        for (int i=0;i<list.length;i++)
            if (list[i]==s)
                return i;
        return -1;
    }
}
