summaryrefslogtreecommitdiffstats
path: root/spec/unit
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2007-09-12 18:32:26 -0500
committerLuke Kanies <luke@madstop.com>2007-09-12 18:32:26 -0500
commit43f22a2414048b180d2c0e2a421fa8d905c4d8eb (patch)
tree72eb0cc1ade37f032284c77ca51a7d1e22d5b317 /spec/unit
parenta6fe70054f4fb3efe4d558ffdd244917ca1c6f9c (diff)
downloadpuppet-43f22a2414048b180d2c0e2a421fa8d905c4d8eb.tar.gz
puppet-43f22a2414048b180d2c0e2a421fa8d905c4d8eb.tar.xz
puppet-43f22a2414048b180d2c0e2a421fa8d905c4d8eb.zip
Adding a to_graph method to TransBuckets, so that the buckets can directly generate a graph, rather than having to first convert to RAL types and then have them convert to a graph. This allows us to make it so components do not need a @children array at all. This was all done because I am having the "already a parent of" problem again, and I have gotten far enough that it is relatively easy to just make this problem go away once and for all.
Diffstat (limited to 'spec/unit')
-rwxr-xr-xspec/unit/other/pgraph.rb310
-rwxr-xr-xspec/unit/other/transbucket.rb123
-rwxr-xr-xspec/unit/other/transobject.rb116
3 files changed, 549 insertions, 0 deletions
diff --git a/spec/unit/other/pgraph.rb b/spec/unit/other/pgraph.rb
new file mode 100755
index 000000000..d4af87ba0
--- /dev/null
+++ b/spec/unit/other/pgraph.rb
@@ -0,0 +1,310 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2007-9-12.
+# Copyright (c) 2006. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/util/graph'
+
+class Container
+ include Puppet::Util::Graph
+ include Enumerable
+ attr_accessor :name
+ def each
+ @children.each do |c| yield c end
+ end
+
+ def initialize(name, ary)
+ @name = name
+ @children = ary
+ end
+
+ def push(*ary)
+ ary.each { |c| @children.push(c)}
+ end
+
+ def to_s
+ @name
+ end
+end
+
+describe Puppet::PGraph do
+ before do
+ @graph = Puppet::PGraph.new
+ end
+
+ it "should correctly clear vertices and edges when asked" do
+ @graph.add_edge!("a", "b")
+ @graph.add_vertex! "c"
+ @graph.clear
+ @graph.vertices.should be_empty
+ @graph.edges.should be_empty
+ end
+end
+
+describe Puppet::PGraph, " when matching edges" do
+ before do
+ @graph = Puppet::PGraph.new
+ @event = Puppet::Event.new(:source => "a", :event => :yay)
+ @none = Puppet::Event.new(:source => "a", :event => :NONE)
+
+ @edges = {}
+ @edges["a/b"] = Puppet::Relationship["a", "b", {:event => :yay, :callback => :refresh}]
+ @edges["a/c"] = Puppet::Relationship["a", "c", {:event => :yay, :callback => :refresh}]
+ @graph.add_edge!(@edges["a/b"])
+ end
+
+ it "should match edges whose source matches the source of the event" do
+ @graph.matching_edges([@event]).should == [@edges["a/b"]]
+ end
+
+ it "should match always match nothing when the event is :NONE" do
+ @graph.matching_edges([@none]).should be_empty
+ end
+
+ it "should match multiple edges" do
+ @graph.add_edge!(@edges["a/c"])
+ @graph.matching_edges([@event]).sort.should == [@edges["a/b"], @edges["a/c"]].sort
+ end
+end
+
+describe Puppet::PGraph, " when determining dependencies" do
+ before do
+ @graph = Puppet::PGraph.new
+
+ @graph.add_edge!("a", "b")
+ @graph.add_edge!("a", "c")
+ @graph.add_edge!("b", "d")
+ end
+
+ it "should find all dependents when they are on multiple levels" do
+ @graph.dependents("a").sort.should == %w{b c d}.sort
+ end
+
+ it "should find single dependents" do
+ @graph.dependents("b").sort.should == %w{d}.sort
+ end
+
+ it "should return an empty array when there are no dependents" do
+ @graph.dependents("c").sort.should == [].sort
+ end
+
+ it "should find all dependencies when they are on multiple levels" do
+ @graph.dependencies("d").sort.should == %w{a b}
+ end
+
+ it "should find single dependencies" do
+ @graph.dependencies("c").sort.should == %w{a}
+ end
+
+ it "should return an empty array when there are no dependencies" do
+ @graph.dependencies("a").sort.should == []
+ end
+end
+
+describe Puppet::PGraph, " when splicing the relationship graph" do
+ def container_graph
+ @one = Container.new("one", %w{a b})
+ @two = Container.new("two", ["c", "d"])
+ @three = Container.new("three", ["i", "j"])
+ @middle = Container.new("middle", ["e", "f", @two])
+ @top = Container.new("top", ["g", "h", @middle, @one, @three])
+ @empty = Container.new("empty", [])
+
+ @contgraph = @top.to_graph
+
+ # We have to add the container to the main graph, else it won't
+ # be spliced in the dependency graph.
+ @contgraph.add_vertex!(@empty)
+ end
+
+ def dependency_graph
+ @depgraph = Puppet::PGraph.new
+ @contgraph.vertices.each do |v|
+ @depgraph.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|
+ @depgraph.add_edge!(source, target, :callback => :refresh)
+ end
+ end
+
+ def splice
+ @depgraph.splice!(@contgraph, Container)
+ end
+
+ before do
+ container_graph
+ dependency_graph
+ splice
+ end
+
+ it "should not create a cyclic graph" do
+ @depgraph.should_not be_cyclic
+ end
+
+ # This is the real heart of splicing -- replacing all containers in
+ # our relationship and exploding their relationships so that each
+ # relationship to a container gets copied to all of its children.
+ it "should remove all Container objects from the dependency graph" do
+ @depgraph.vertices.find_all { |v| v.is_a?(Container) }.should be_empty
+ end
+
+ it "should add container relationships to contained objects" do
+ @contgraph.leaves(@middle).each do |leaf|
+ @depgraph.should be_edge("h", leaf)
+ end
+ end
+
+ it "should explode container-to-container relationships, making edges between all respective contained objects" do
+ @one.each do |oobj|
+ @two.each do |tobj|
+ @depgraph.should be_edge(oobj, tobj)
+ end
+ end
+ end
+
+ it "should no longer contain anything but the non-container objects" do
+ @depgraph.vertices.find_all { |v| ! v.is_a?(String) }.should be_empty
+ end
+
+ it "should copy labels" do
+ @depgraph.edges.each do |edge|
+ edge.label.should == {:callback => :refresh}
+ end
+ end
+
+ it "should not add labels to edges that have none" do
+ @depgraph.add_edge!(@two, @three)
+ splice
+ @depgraph.edge_label("c", "i").should == {}
+ end
+
+ it "should copy labels over edges that have none" do
+ @depgraph.add_edge!("c", @three, {:callback => :refresh})
+ splice
+ # And make sure the label got copied.
+ @depgraph.edge_label("c", "i").should == {:callback => :refresh}
+ end
+
+ it "should not replace a label with a nil label" do
+ # Lastly, add some new label-less edges and make sure the label stays.
+ @depgraph.add_edge!(@middle, @three)
+ @depgraph.add_edge!("c", @three, {:callback => :refresh})
+ splice
+ @depgraph.edge_label("c", "i").should == {:callback => :refresh}
+ end
+
+ it "should copy labels to all created edges" do
+ @depgraph.add_edge!(@middle, @three)
+ @depgraph.add_edge!("c", @three, {:callback => :refresh})
+ splice
+ @three.each do |child|
+ edge = @depgraph.edge_class.new("c", child)
+ @depgraph.should be_edge(edge)
+ @depgraph[edge].should == {:callback => :refresh}
+ end
+ end
+end
+
+# Labels in this graph are used for managing relationships,
+# including callbacks, so they're quite important.
+describe Puppet::PGraph, " when managing labels" do
+ before do
+ @graph = Puppet::PGraph.new
+ @label = {:callback => :yay}
+ end
+
+ it "should return nil for edges with no label" do
+ @graph.add_edge!(:a, :b)
+ @graph.edge_label(:a, :b).should be_nil
+ end
+
+ it "should just return empty label hashes" do
+ @graph.add_edge!(:a, :b, {})
+ @graph.edge_label(:a, :b).should == {}
+ end
+
+ it "should consider empty label hashes to be nil when copying" do
+ @graph.add_edge!(:a, :b)
+ @graph.copy_label(:a, :b, {})
+ @graph.edge_label(:a, :b).should be_nil
+ end
+
+ it "should return label hashes" do
+ @graph.add_edge!(:a, :b, @label)
+ @graph.edge_label(:a, :b).should == @label
+ end
+
+ it "should replace nil labels with real labels" do
+ @graph.add_edge!(:a, :b)
+ @graph.copy_label(:a, :b, @label)
+ @graph.edge_label(:a, :b).should == @label
+ end
+
+ it "should not replace labels with nil labels" do
+ @graph.add_edge!(:a, :b, @label)
+ @graph.copy_label(:a, :b, {})
+ @graph.edge_label(:a, :b).should == @label
+ end
+end
+
+describe Puppet::PGraph, " when sorting the graph" do
+ before do
+ @graph = Puppet::PGraph.new
+ end
+
+ def add_edges(hash)
+ hash.each do |a,b|
+ @graph.add_edge!(a, b)
+ end
+ end
+
+ it "should fail on two-vertex loops" do
+ add_edges :a => :b, :b => :a
+ proc { @graph.topsort }.should raise_error(Puppet::Error)
+ end
+
+ it "should fail on multi-vertex loops" do
+ add_edges :a => :b, :b => :c, :c => :a
+ proc { @graph.topsort }.should raise_error(Puppet::Error)
+ end
+
+ it "should fail when a larger tree contains a small cycle" do
+ add_edges :a => :b, :b => :a, :c => :a, :d => :c
+ proc { @graph.topsort }.should raise_error(Puppet::Error)
+ end
+
+ it "should succeed on trees with no cycles" do
+ add_edges :a => :b, :b => :e, :c => :a, :d => :c
+ proc { @graph.topsort }.should_not raise_error
+ end
+end
+
+describe Puppet::PGraph, " functioning as a resource container" do
+ before do
+ @graph = Puppet::PGraph.new
+ @one = stub 'resource1', :ref => "Me[you]"
+ @two = stub 'resource2', :ref => "Me[him]"
+ @dupe = stub 'resource3', :ref => "Me[you]"
+ end
+
+ it "should make all vertices available by resource reference" do
+ @graph.add_vertex!(@one)
+ @graph.resource(@one.ref).should equal(@one)
+ end
+
+ it "should not allow two resources with the same resource reference" do
+ @graph.add_vertex!(@one)
+ proc { @graph.add_vertex!(@dupe) }.should raise_error(ArgumentError)
+ end
+
+ it "should not store objects that do not respond to :ref" do
+ str = "thing"
+ @graph.add_vertex!(str)
+ @graph.resource(str).should be_nil
+ end
+end
diff --git a/spec/unit/other/transbucket.rb b/spec/unit/other/transbucket.rb
new file mode 100755
index 000000000..c013973ee
--- /dev/null
+++ b/spec/unit/other/transbucket.rb
@@ -0,0 +1,123 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::TransBucket do
+ before do
+ @bucket = Puppet::TransBucket.new
+ end
+
+ it "should be able to produce a RAL component" do
+ @bucket.name = "luke"
+ @bucket.type = "user"
+
+ resource = nil
+ proc { resource = @bucket.to_type }.should_not raise_error
+ resource.should be_instance_of(Puppet::Type::Component)
+ resource.title.should == "user[luke]"
+ end
+
+ it "should accept TransObjects into its children list" do
+ object = Puppet::TransObject.new("luke", "user")
+ proc { @bucket.push(object) }.should_not raise_error
+ @bucket.each do |o|
+ o.should equal(object)
+ end
+ end
+
+ it "should accept TransBuckets into its children list" do
+ object = Puppet::TransBucket.new()
+ proc { @bucket.push(object) }.should_not raise_error
+ @bucket.each do |o|
+ o.should equal(object)
+ end
+ end
+
+ it "should refuse to accept any children that are not TransObjects or TransBuckets" do
+ proc { @bucket.push "a test" }.should raise_error
+ end
+
+ it "should return nil as its reference when type or name is missing" do
+ @bucket.to_ref.should be_nil
+ end
+
+ it "should return the title as its reference" do
+ @bucket.name = "luke"
+ @bucket.type = "user"
+ @bucket.to_ref.should == "user[luke]"
+ end
+end
+
+describe Puppet::TransBucket, " when generating a resource graph" do
+ before do
+ @bottom = Puppet::TransBucket.new
+ @bottom.type = "fake"
+ @bottom.name = "bottom"
+ @bottomobj = Puppet::TransObject.new("bottom", "user")
+ @bottom.push @bottomobj
+
+ @middle = Puppet::TransBucket.new
+ @middle.type = "fake"
+ @middle.name = "middle"
+ @middleobj = Puppet::TransObject.new("middle", "user")
+ @middle.push(@middleobj)
+ @middle.push(@bottom)
+
+ @top = Puppet::TransBucket.new
+ @top.type = "fake"
+ @top.name = "top"
+ @topobj = Puppet::TransObject.new("top", "user")
+ @top.push(@topobj)
+ @top.push(@middle)
+
+ @graph = @top.to_graph
+
+ @users = %w{top middle bottom}
+ @fakes = %w{fake[bottom] fake[middle] fake[top]}
+ end
+
+ it "should convert all transportable objects to RAL resources" do
+ @users.each do |name|
+ @graph.vertices.find { |r| r.class.name == :user and r.title == name }.should be_instance_of(Puppet::Type.type(:user))
+ end
+ end
+
+ it "should convert all transportable buckets to RAL components" do
+ @fakes.each do |name|
+ @graph.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component))
+ end
+ end
+
+ it "should add all resources to the graph's resource table" do
+ @graph.resource("fake[top]").should equal(@top)
+ end
+
+ after do
+ Puppet::Type.allclear
+ end
+end
+
+describe Puppet::TransBucket, " when serializing" do
+ before do
+ @bucket = Puppet::TransBucket.new(%w{one two})
+ @bucket.name = "one"
+ @bucket.type = "two"
+ end
+
+ it "should be able to be dumped to yaml" do
+ proc { YAML.dump(@bucket) }.should_not raise_error
+ end
+
+ it "should dump YAML that produces an equivalent object" do
+ result = YAML.dump(@bucket)
+
+ newobj = YAML.load(result)
+ newobj.name.should == "one"
+ newobj.type.should == "two"
+ children = []
+ newobj.each do |o|
+ children << o
+ end
+ children.should == %w{one two}
+ end
+end
diff --git a/spec/unit/other/transobject.rb b/spec/unit/other/transobject.rb
new file mode 100755
index 000000000..07c9dc761
--- /dev/null
+++ b/spec/unit/other/transobject.rb
@@ -0,0 +1,116 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::TransObject, " when building its search path" do
+end
+
+describe Puppet::TransObject, " when building its search path" do
+end
+#!/usr/bin/env ruby
+
+$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
+
+require 'puppet'
+require 'puppet/transportable'
+require 'puppettest'
+require 'puppettest/parsertesting'
+require 'yaml'
+
+class TestTransportable < Test::Unit::TestCase
+ include PuppetTest::ParserTesting
+
+ def test_yamldumpobject
+ obj = mk_transobject
+ obj.to_yaml_properties
+ str = nil
+ assert_nothing_raised {
+ str = YAML.dump(obj)
+ }
+
+ newobj = nil
+ assert_nothing_raised {
+ newobj = YAML.load(str)
+ }
+
+ assert(newobj.name, "Object has no name")
+ assert(newobj.type, "Object has no type")
+ end
+
+ def test_yamldumpbucket
+ objects = %w{/etc/passwd /etc /tmp /var /dev}.collect { |d|
+ mk_transobject(d)
+ }
+ bucket = mk_transbucket(*objects)
+ str = nil
+ assert_nothing_raised {
+ str = YAML.dump(bucket)
+ }
+
+ newobj = nil
+ assert_nothing_raised {
+ newobj = YAML.load(str)
+ }
+
+ assert(newobj.name, "Bucket has no name")
+ assert(newobj.type, "Bucket has no type")
+ end
+
+ # Verify that we correctly strip out collectable objects, since they should
+ # not be sent to the client.
+ def test_collectstrip
+ top = mk_transtree do |object, depth, width|
+ if width % 2 == 1
+ object.collectable = true
+ end
+ end
+
+ assert(top.flatten.find_all { |o| o.collectable }.length > 0,
+ "Could not find any collectable objects")
+
+ # Now strip out the collectable objects
+ top.collectstrip!
+
+ # And make sure they're actually gone
+ assert_equal(0, top.flatten.find_all { |o| o.collectable }.length,
+ "Still found collectable objects")
+ end
+
+ # Make sure our 'delve' command is working
+ def test_delve
+ top = mk_transtree do |object, depth, width|
+ if width % 2 == 1
+ object.collectable = true
+ end
+ end
+
+ objects = []
+ buckets = []
+ collectable = []
+
+ count = 0
+ assert_nothing_raised {
+ top.delve do |object|
+ count += 1
+ if object.is_a? Puppet::TransBucket
+ buckets << object
+ else
+ objects << object
+ if object.collectable
+ collectable << object
+ end
+ end
+ end
+ }
+
+ top.flatten.each do |obj|
+ assert(objects.include?(obj), "Missing obj %s[%s]" % [obj.type, obj.name])
+ end
+
+ assert_equal(collectable.length,
+ top.flatten.find_all { |o| o.collectable }.length,
+ "Found incorrect number of collectable objects")
+ end
+end
+
+# $Id$