diff options
| author | Luke Kanies <luke@madstop.com> | 2007-09-12 18:32:26 -0500 |
|---|---|---|
| committer | Luke Kanies <luke@madstop.com> | 2007-09-12 18:32:26 -0500 |
| commit | 43f22a2414048b180d2c0e2a421fa8d905c4d8eb (patch) | |
| tree | 72eb0cc1ade37f032284c77ca51a7d1e22d5b317 /spec/unit | |
| parent | a6fe70054f4fb3efe4d558ffdd244917ca1c6f9c (diff) | |
| download | puppet-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-x | spec/unit/other/pgraph.rb | 310 | ||||
| -rwxr-xr-x | spec/unit/other/transbucket.rb | 123 | ||||
| -rwxr-xr-x | spec/unit/other/transobject.rb | 116 |
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$ |
