diff options
| author | Paul Berry <paul@puppetlabs.com> | 2010-08-13 15:43:34 -0700 |
|---|---|---|
| committer | Paul Berry <paul@puppetlabs.com> | 2010-08-13 15:54:26 -0700 |
| commit | 4da88fb4cd57871f16649d50572240ac3f7420f0 (patch) | |
| tree | 1b0df4e444bc27f925aac293cf721fa7acee06f7 /spec/unit/parser | |
| parent | caca187dffbd6e628d7eda599c7f2939dd05fafc (diff) | |
| download | puppet-4da88fb4cd57871f16649d50572240ac3f7420f0.tar.gz puppet-4da88fb4cd57871f16649d50572240ac3f7420f0.tar.xz puppet-4da88fb4cd57871f16649d50572240ac3f7420f0.zip | |
[#4496]+[#4521]+[#4522] Add structures to the AST to represent type definitions (classes, definitions, and nodes).
Previously, type definitions were not represented directly in the AST.
Instead, the parser would instantiate types and insert them into
known_resource_types as soon as they were parsed. This made it
difficult to distinguish which types had come from the file that was
just parsed and which types had been loaded previously, which led to
bug 4496.
A side-effect of this change is that the user is no longer allowed to
define types inside of conditional constructs (such as if/else). This
was allowed before but had unexpected semantics (bugs 4521 and 4522).
It is still possible, however, to place an "include" statement inside
a conditional construct, and have that "include" statement trigger the
autoloading of a file that instantiates types.
Diffstat (limited to 'spec/unit/parser')
| -rw-r--r-- | spec/unit/parser/ast/definition_spec.rb | 17 | ||||
| -rw-r--r-- | spec/unit/parser/ast/hostclass_spec.rb | 73 | ||||
| -rw-r--r-- | spec/unit/parser/ast/node_spec.rb | 30 | ||||
| -rwxr-xr-x | spec/unit/parser/ast/resource_spec.rb | 6 | ||||
| -rwxr-xr-x | spec/unit/parser/parser_spec.rb | 96 | ||||
| -rwxr-xr-x | spec/unit/parser/scope_spec.rb | 3 | ||||
| -rw-r--r-- | spec/unit/parser/type_loader_spec.rb | 32 |
7 files changed, 189 insertions, 68 deletions
diff --git a/spec/unit/parser/ast/definition_spec.rb b/spec/unit/parser/ast/definition_spec.rb new file mode 100644 index 000000000..01ca068ad --- /dev/null +++ b/spec/unit/parser/ast/definition_spec.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Definition do + describe "when instantiated" do + it "should create a definition with the proper type, name, context, and module name" do + definition = Puppet::Parser::AST::Definition.new('foo', :line => 5) + instantiated_definitions = definition.instantiate('modname') + instantiated_definitions.length.should == 1 + instantiated_definitions[0].type.should == :definition + instantiated_definitions[0].name.should == 'foo' + instantiated_definitions[0].line.should == 5 + instantiated_definitions[0].module_name.should == 'modname' + end + end +end diff --git a/spec/unit/parser/ast/hostclass_spec.rb b/spec/unit/parser/ast/hostclass_spec.rb new file mode 100644 index 000000000..b22eba98b --- /dev/null +++ b/spec/unit/parser/ast/hostclass_spec.rb @@ -0,0 +1,73 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Hostclass do + def ast + Puppet::Parser::AST + end + + def newarray(*elems) + ast::ASTArray.new({}).push(*elems) + end + + it "should make its name and context available through accessors" do + hostclass = ast::Hostclass.new('foo', :line => 5) + hostclass.name.should == 'foo' + hostclass.context.should == {:line => 5} + end + + it "should make its code available through an accessor" do + code = newarray + hostclass = ast::Hostclass.new('foo', :code => code) + hostclass.code.should be_equal(code) + end + + describe "when instantiated" do + it "should create a class with the proper type, code, name, context, and module name" do + code = newarray + hostclass = ast::Hostclass.new('foo', :code => code, :line => 5) + instantiated_class = hostclass.instantiate('modname')[0] + instantiated_class.type.should == :hostclass + instantiated_class.name.should == 'foo' + instantiated_class.code.should be_equal(code) + instantiated_class.line.should == 5 + instantiated_class.module_name.should == 'modname' + end + + it "should instantiate all nested classes, defines, and nodes with the same module name." do + nested_objects = newarray(ast::Hostclass.new('foo::child1'), + ast::Definition.new('foo::child2'), + ast::Definition.new('child3')) + hostclass = ast::Hostclass.new('foo', :code => nested_objects) + instantiated_classes = hostclass.instantiate('modname') + instantiated_classes.length.should == 4 + instantiated_classes[0].name.should == 'foo' + instantiated_classes[1].name.should == 'foo::child1' + instantiated_classes[2].name.should == 'foo::child2' + instantiated_classes[3].name.should == 'child3' + instantiated_classes.each { |cls| cls.module_name.should == 'modname' } + end + + it "should handle a nested class that contains its own nested classes." do + foo_bar_baz = ast::Hostclass.new('foo::bar::baz') + foo_bar = ast::Hostclass.new('foo::bar', :code => newarray(foo_bar_baz)) + foo = ast::Hostclass.new('foo', :code => newarray(foo_bar)) + instantiated_classes = foo.instantiate('') + instantiated_classes.length.should == 3 + instantiated_classes[0].name.should == 'foo' + instantiated_classes[1].name.should == 'foo::bar' + instantiated_classes[2].name.should == 'foo::bar::baz' + end + + it "should skip nested elements that are not classes, definitions, or nodes." do + func = ast::Function.new(:name => 'biz', :arguments => newarray(ast::Name.new(:value => 'baz'))) + foo = ast::Hostclass.new('foo', :code => newarray(func)) + instantiated_classes = foo.instantiate('') + instantiated_classes.length.should == 1 + instantiated_classes[0].should be_a(Puppet::Resource::Type) + instantiated_classes[0].name.should == 'foo' + end + end +end + diff --git a/spec/unit/parser/ast/node_spec.rb b/spec/unit/parser/ast/node_spec.rb new file mode 100644 index 000000000..409e877f9 --- /dev/null +++ b/spec/unit/parser/ast/node_spec.rb @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Node do + describe "when instantiated" do + it "should make its names available through an accessor" do + node = Puppet::Parser::AST::Node.new(['foo', 'bar']) + node.names.should == ['foo', 'bar'] + end + + it "should create a node with the proper type, name, context, and module name" do + node = Puppet::Parser::AST::Node.new(['foo'], :line => 5) + instantiated_nodes = node.instantiate('modname') + instantiated_nodes.length.should == 1 + instantiated_nodes[0].type.should == :node + instantiated_nodes[0].name.should == 'foo' + instantiated_nodes[0].line.should == 5 + instantiated_nodes[0].module_name.should == 'modname' + end + + it "should handle multiple names" do + node = Puppet::Parser::AST::Node.new(['foo', 'bar']) + instantiated_nodes = node.instantiate('modname') + instantiated_nodes.length.should == 2 + instantiated_nodes[0].name.should == 'foo' + instantiated_nodes[1].name.should == 'bar' + end + end +end diff --git a/spec/unit/parser/ast/resource_spec.rb b/spec/unit/parser/ast/resource_spec.rb index 58ffae925..4e5549b96 100755 --- a/spec/unit/parser/ast/resource_spec.rb +++ b/spec/unit/parser/ast/resource_spec.rb @@ -89,9 +89,9 @@ describe Puppet::Parser::AST::Resource do before do @scope = Puppet::Parser::Scope.new :compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new) - @parser.newdefine "one" - @parser.newdefine "one::two" - @parser.newdefine "three" + ["one", "one::two", "three"].each do |name| + @parser.environment.known_resource_types.add(Puppet::Resource::Type.new(:definition, name, {})) + end @twoscope = @scope.newscope(:namespace => "one") @twoscope.resource = @scope.resource end diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index 0657ab37a..0a61e73de 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -73,14 +73,16 @@ describe Puppet::Parser do lambda { @parser.parse("$var += ") }.should raise_error end - it "should call ast::VarDef with append=true" do - ast::VarDef.expects(:new).with { |h| h[:append] == true } - @parser.parse("$var += 2") + it "should create ast::VarDef with append=true" do + vardef = @parser.parse("$var += 2").code[0] + vardef.should be_a(Puppet::Parser::AST::VarDef) + vardef.append.should == true end it "should work with arrays too" do - ast::VarDef.expects(:new).with { |h| h[:append] == true } - @parser.parse("$var += ['test']") + vardef = @parser.parse("$var += ['test']").code[0] + vardef.should be_a(Puppet::Parser::AST::VarDef) + vardef.append.should == true end end @@ -160,10 +162,14 @@ describe Puppet::Parser do end it "should create an ast::ResourceOverride" do - ast::ResourceOverride.expects(:new).with { |arg| - arg[:line]==1 and arg[:object].is_a?(ast::ResourceReference) and arg[:parameters].is_a?(ast::ResourceParam) - } - @parser.parse('Resource["title1","title2"] { param => value }') + #ast::ResourceOverride.expects(:new).with { |arg| + # arg[:line]==1 and arg[:object].is_a?(ast::ResourceReference) and arg[:parameters].is_a?(ast::ResourceParam) + #} + ro = @parser.parse('Resource["title1","title2"] { param => value }').code[0] + ro.should be_a(ast::ResourceOverride) + ro.line.should == 1 + ro.object.should be_a(ast::ResourceReference) + ro.parameters[0].should be_a(ast::ResourceParam) end end @@ -290,24 +296,6 @@ describe Puppet::Parser do end end - describe "when creating a node" do - before :each do - @lexer = stub 'lexer' - @lexer.stubs(:getcomment) - @parser.stubs(:lexer).returns(@lexer) - @node = stub_everything 'node' - @parser.stubs(:ast_context).returns({}) - @parser.stubs(:node).returns(nil) - - @nodename = stub 'nodename', :is_a? => false, :value => "foo" - @nodename.stubs(:is_a?).with(Puppet::Parser::AST::HostName).returns(true) - end - - it "should return an array of nodes" do - @parser.newnode(@nodename).should be_instance_of(Array) - end - end - describe "when retrieving a specific node" do it "should delegate to the known_resource_types node" do @known_resource_types.expects(:node).with("node") @@ -360,30 +348,28 @@ describe Puppet::Parser do @parser.stubs(:known_resource_types).returns @krt end - it "should create new classes" do - @parser.parse("class foobar {}") - @krt.hostclass("foobar").should be_instance_of(Puppet::Resource::Type) + it "should not create new classes" do + @parser.parse("class foobar {}").code[0].should be_a(Puppet::Parser::AST::Hostclass) + @krt.hostclass("foobar").should be_nil end it "should correctly set the parent class when one is provided" do - @parser.parse("class foobar inherits yayness {}") - @krt.hostclass("foobar").parent.should == "yayness" + @parser.parse("class foobar inherits yayness {}").code[0].instantiate('')[0].parent.should == "yayness" end it "should correctly set the parent class for multiple classes at a time" do - @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}") - @krt.hostclass("foobar").parent.should == "yayness" - @krt.hostclass("boo").parent.should == "bar" + statements = @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}").code + statements[0].instantiate('')[0].parent.should == "yayness" + statements[1].instantiate('')[0].parent.should == "bar" end it "should define the code when some is provided" do - @parser.parse("class foobar { $var = val }") - @krt.hostclass("foobar").code.should_not be_nil + @parser.parse("class foobar { $var = val }").code[0].code.should_not be_nil end it "should define parameters when provided" do - @parser.parse("class foobar($biz,$baz) {}") - @krt.hostclass("foobar").arguments.should == {"biz" => nil, "baz" => nil} + foobar = @parser.parse("class foobar($biz,$baz) {}").code[0].instantiate('')[0] + foobar.arguments.should == {"biz" => nil, "baz" => nil} end end @@ -400,13 +386,37 @@ describe Puppet::Parser do end it "should correctly mark exported resources as exported" do - @parser.parse("@@file { '/file': }") - @krt.hostclass("").code[0].exported.should be_true + @parser.parse("@@file { '/file': }").code[0][0].exported.should be_true end it "should correctly mark virtual resources as virtual" do - @parser.parse("@file { '/file': }") - @krt.hostclass("").code[0].virtual.should be_true + @parser.parse("@file { '/file': }").code[0][0].virtual.should be_true + end + end + + describe "when parsing nodes" do + it "should be able to parse a node with a single name" do + node = @parser.parse("node foo { }").code[0] + node.should be_a Puppet::Parser::AST::Node + node.names.length.should == 1 + node.names[0].value.should == "foo" + end + + it "should be able to parse a node with two names" do + node = @parser.parse("node foo, bar { }").code[0] + node.should be_a Puppet::Parser::AST::Node + node.names.length.should == 2 + node.names[0].value.should == "foo" + node.names[1].value.should == "bar" + end + + it "should be able to parse a node with three names" do + node = @parser.parse("node foo, bar, baz { }").code[0] + node.should be_a Puppet::Parser::AST::Node + node.names.length.should == 3 + node.names[0].value.should == "foo" + node.names[1].value.should == "bar" + node.names[2].value.should == "baz" end end end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 9895f446b..2e390a53b 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -29,8 +29,7 @@ describe Puppet::Parser::Scope do end it "should be able to retrieve its parent module name from the source of its parent type" do - @topscope.source = Puppet::Resource::Type.new(:hostclass, :foo) - @topscope.source.module_name = "foo" + @topscope.source = Puppet::Resource::Type.new(:hostclass, :foo, :module_name => "foo") @scope.parent_module_name.should == "foo" end diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb index b7e174753..b06251681 100644 --- a/spec/unit/parser/type_loader_spec.rb +++ b/spec/unit/parser/type_loader_spec.rb @@ -28,26 +28,18 @@ describe Puppet::Parser::TypeLoader do describe "when loading names from namespaces" do it "should do nothing if the name to import is an empty string" do @loader.expects(:name2files).never - @loader.try_load_fqname("") { |filename, modname| raise :should_not_occur }.should be_nil + @loader.try_load_fqname(:hostclass, "") { |filename, modname| raise :should_not_occur }.should be_nil end it "should attempt to import each generated name" do - @loader.expects(:import).with("foo/bar",nil) - @loader.expects(:import).with("foo",nil) - @loader.try_load_fqname("foo::bar") { |f| false } - end - - it "should yield after each import" do - yielded = [] - @loader.expects(:import).with("foo/bar",nil) - @loader.expects(:import).with("foo",nil) - @loader.try_load_fqname("foo::bar") { |filename, modname| yielded << [filename, modname]; false } - yielded.should == [["foo/bar", nil], ["foo", nil]] + @loader.expects(:import).with("foo/bar",nil).returns([]) + @loader.expects(:import).with("foo",nil).returns([]) + @loader.try_load_fqname(:hostclass, "foo::bar") { |f| false } end it "should know when a given name has been loaded" do - @loader.expects(:import).with("file",nil) - @loader.try_load_fqname("file") { |f| true } + @loader.expects(:import).with("file",nil).returns([]) + @loader.try_load_fqname(:hostclass, "file") { |f| true } @loader.should be_loaded("file") end end @@ -55,7 +47,7 @@ describe Puppet::Parser::TypeLoader do describe "when importing" do before do Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}] - @loader.stubs(:parse_file) + @loader.stubs(:parse_file).returns(Puppet::Parser::AST::Hostclass.new('')) end it "should return immediately when imports are being ignored" do @@ -86,13 +78,13 @@ describe Puppet::Parser::TypeLoader do it "should parse each found file" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] - @loader.expects(:parse_file).with("/one") + @loader.expects(:parse_file).with("/one").returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile") end it "should make each file qualified before attempting to parse it" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}] - @loader.expects(:parse_file).with("/current/one") + @loader.expects(:parse_file).with("/current/one").returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile", "/current/file") end @@ -105,7 +97,7 @@ describe Puppet::Parser::TypeLoader do it "should not attempt to import files that have already been imported" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] - @loader.expects(:parse_file).once + @loader.expects(:parse_file).once.returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile") # This will fail if it tries to reimport the file. @@ -116,7 +108,7 @@ describe Puppet::Parser::TypeLoader do describe "when parsing a file" do before do @parser = Puppet::Parser::Parser.new(@loader.environment) - @parser.stubs(:parse) + @parser.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) @parser.stubs(:file=) Puppet::Parser::Parser.stubs(:new).with(@loader.environment).returns @parser end @@ -128,7 +120,7 @@ describe Puppet::Parser::TypeLoader do it "should assign the parser its file and parse" do @parser.expects(:file=).with("/my/file") - @parser.expects(:parse) + @parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) @loader.parse_file("/my/file") end end |
