#!/usr/bin/env ruby
#
#  Created by Luke Kanies on 2006-11-16.
#  Copyright (c) 2006. All rights reserved.

$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/

require 'puppettest'
require 'puppettest/graph'

class TestPGraph < Test::Unit::TestCase
	include PuppetTest
	include PuppetTest::Graph
	
	Edge = Puppet::Relationship
	
	def test_clear
	    graph = Puppet::PGraph.new
	    graph.add_edge!("a", "b")
	    graph.add_vertex! "c"
	    assert_nothing_raised do
	        graph.clear
        end
        assert(graph.vertices.empty?, "Still have vertices after clear")
        assert(graph.edges.empty?, "still have edges after clear")
    end
	    
	
	def test_matching_edges
	    graph = Puppet::PGraph.new
	    
	    event = Puppet::Event.new(:source => "a", :event => :yay)
	    none = Puppet::Event.new(:source => "a", :event => :NONE)
	    
	    edges = {}
	    
	    edges["a/b"] = Edge["a", "b", {:event => :yay, :callback => :refresh}]
	    edges["a/c"] = Edge["a", "c", {:event => :yay, :callback => :refresh}]
	    
	    graph.add_edge!(edges["a/b"])
	    
	    # Try it for the trivial case of one target and a matching event
	    assert_equal([edges["a/b"]], graph.matching_edges([event]))
	    
	    # Make sure we get nothing with a different event
	    assert_equal([], graph.matching_edges([none]))
	    
	    # Set up multiple targets and make sure we get them all back
	    graph.add_edge!(edges["a/c"])
	    assert_equal([edges["a/b"], edges["a/c"]].sort, graph.matching_edges([event]).sort)
	    assert_equal([], graph.matching_edges([none]))
    end
    
    def test_dependencies
        graph = Puppet::PGraph.new
        
        graph.add_edge!("a", "b")
        graph.add_edge!("a", "c")
        graph.add_edge!("b", "d")
        
        assert_equal(%w{b c d}.sort, graph.dependents("a").sort)
        assert_equal(%w{d}.sort, graph.dependents("b").sort)
        assert_equal([].sort, graph.dependents("c").sort)
        
        assert_equal(%w{a b}, graph.dependencies("d").sort)
        assert_equal(%w{a}, graph.dependencies("b").sort)
        assert_equal(%w{a}, graph.dependencies("c").sort)
        assert_equal([], graph.dependencies("a").sort)
        
    end
    
    # Test that we can take a containment graph and rearrange it by dependencies
    def test_splice
        one, two, three, middle, top = build_tree
        empty = Container.new("empty", [])
        # Also, add an empty container to top
        top.push empty

        contgraph = top.to_graph

        # Now add a couple of child files, so that we can test whether all
        # containers get spliced, rather than just components.

        # Now make a dependency graph
        deps = Puppet::PGraph.new

        contgraph.vertices.each do |v|
            deps.add_vertex(v)
        end

        # We have to specify a relationship to our empty container, else it
        # never makes it into the dep graph in the first place.
        {one => two, "f" => "c", "h" => middle, "c" => empty}.each do |source, target|
            deps.add_edge!(source, target, :callback => :refresh)
        end
        
        #contgraph.to_jpg(File.expand_path("~/Desktop/pics"), "main")
        #deps.to_jpg(File.expand_path("~/Desktop/pics"), "before")
        assert_nothing_raised { deps.splice!(contgraph, Container) }
        
        assert(! deps.cyclic?, "Created a cyclic graph")

        # Make sure there are no container objects remaining
        #deps.to_jpg(File.expand_path("~/Desktop/pics"), "after")
        c = deps.vertices.find_all { |v| v.is_a?(Container) }
        assert(c.empty?, "Still have containers %s" % c.inspect)
        
        # Now make sure the containers got spliced correctly.
        contgraph.leaves(middle).each do |leaf|
            assert(deps.edge?("h", leaf), "no edge for h => %s" % leaf)
        end
        one.each do |oobj|
            two.each do |tobj|
                assert(deps.edge?(oobj, tobj), "no %s => %s edge" % [oobj, tobj])
            end
        end
        
        nons = deps.vertices.find_all { |v| ! v.is_a?(String) }
        assert(nons.empty?,
            "still contain non-strings %s" % nons.inspect)
        
        deps.edges.each do |edge|
            assert_equal({:callback => :refresh}, edge.label,
                "Label was not copied for %s => %s" % [edge.source, edge.target])
        end

        # Now add some relationships to three, but only add labels to one of
        # the relationships.

        # Add a simple, label-less relationship
        deps.add_edge!(two, three)
        assert_nothing_raised { deps.splice!(contgraph, Container) }

        # And make sure it stuck, with no labels.
        assert_equal({}, deps.edge_label("c", "i"),
            "label was created for c => i")

        # Now add some edges with labels, in a way that should overwrite
        deps.add_edge!("c", three, {:callback => :refresh})
        assert_nothing_raised { deps.splice!(contgraph, Container) }

        # And make sure the label got copied.
        assert_equal({:callback => :refresh}, deps.edge_label("c", "i"),
            "label was not copied for c => i")

        # Lastly, add some new label-less edges and make sure the label stays.
        deps.add_edge!(middle, three)
        assert_nothing_raised { deps.splice!(contgraph, Container) }
        assert_equal({:callback => :refresh}, deps.edge_label("c", "i"),
            "label was lost for c => i")
        
        # Now make sure the 'three' edges all have the label we've used.
        # Note that this will not work when we support more than one type of
        # subscription.
        three.each do |child|
            edge = deps.edge_class.new("c", child)
            assert(deps.edge?(edge), "no c => %s edge" % child)
            assert_equal({:callback => :refresh}, deps[edge],
                "label was not retained for c => %s" % child)
        end
    end

    def test_copy_label
        graph = Puppet::PGraph.new

        # First make an edge with no label
        graph.add_edge!(:a, :b)
        assert_nil(graph.edge_label(:a, :b), "Created a label")

        # Now try to copy an empty label in.
        graph.copy_label(:a, :b, {})

        # It should just do nothing, since we copied an empty label.
        assert_nil(graph.edge_label(:a, :b), "Created a label")

        # Now copy in a real label.
        graph.copy_label(:a, :b, {:callback => :yay})
        assert_equal({:callback => :yay},
            graph.edge_label(:a, :b), "Did not copy label")

        # Now copy in a nil label
        graph.copy_label(:a, :b, nil)
        assert_equal({:callback => :yay},
            graph.edge_label(:a, :b), "lost label")

        # And an empty one.
        graph.copy_label(:a, :b, {})
        assert_equal({:callback => :yay},
            graph.edge_label(:a, :b), "lost label")
    end

    def test_check_cycle
        {
            {:a => :b, :b => :a} => true,
            {:a => :b, :b => :c, :c => :a} => true,
            {:a => :b, :b => :c} => false,
        }.each do |hash, result|
            graph = Puppet::PGraph.new
            hash.each do |a,b|
                graph.add_edge!(a, b)
            end

            if result
                assert_raise(Puppet::Error, "%s did not fail" % hash.inspect) do
                    graph.check_cycle(graph.topsort)
                end
            else
                assert_nothing_raised("%s failed" % hash.inspect) do
                    graph.check_cycle(graph.topsort)
                end
            end
        end
    end

    # This isn't really a unit test, it's just a way to do some graphing with
    # tons of relationships so we can see how it performs.
    def disabled_test_lots_of_relationships
        containers = Puppet::PGraph.new
        relationships = Puppet::PGraph.new
        labels = %w{a b c d e}
        conts = {}
        vertices = {}
        labels.each do |label|
            vertices[label] = []
        end
        num = 100
        num.times do |i|
            labels.each do |label|
                vertices[label] << ("%s%s" % [label, i])
            end
        end
        labels.each do |label|
            conts[label] = Container.new(label, vertices[label])
        end

        conts.each do |label, cont|
            cont.each do |child|
                containers.add_edge!(cont, child)
            end
        end
        prev = nil
        labels.inject(nil) do |prev, label|
            if prev
                containers.add_edge!(conts[prev], conts[label])
            end
            label
        end

        containers.to_jpg(File.expand_path("~/Desktop/pics/lots"), "start")

        # Now create the relationship graph

        # Make everything in both b and c require d1
        %w{b c}.each do |label|
            conts[label].each do |v|
                relationships.add_edge!(v, "d1")
                #relationships.add_edge!(v, conts["d"])
            end
        end

        # Make most in b also require the appropriate thing in c
        conts["b"].each do |v|
            i = v.split('')[1]

            relationships.add_edge!(v, "c%s" % i)
        end

        # And make d1 require most of e
        num.times do |i|
            relationships.add_edge!("d1", "e%s" % i)
        end

        containers.vertices.each do |v|
            relationships.add_vertex!(v)
        end
        relationships.to_jpg(File.expand_path("~/Desktop/pics/lots"), "relationships")

        time = Benchmark.realtime do
            relationships.splice!(containers, Container)
        end
        relationships.to_jpg(File.expand_path("~/Desktop/pics/lots"), "final")
        puts time
        time = Benchmark.realtime do
            relationships.topsort
        end
    end
end

# $Id: pgraph.rb 2230 2007-02-27 20:34:32Z luke $


syntax highlighted by Code2HTML, v. 0.9.1