diff --git a/lib/puppet/parser/ast/casestatement.rb b/lib/puppet/parser/ast/casestatement.rb index 25b0fc691..64298cac3 100644 --- a/lib/puppet/parser/ast/casestatement.rb +++ b/lib/puppet/parser/ast/casestatement.rb @@ -1,44 +1,42 @@ require 'puppet/parser/ast/branch' class Puppet::Parser::AST # The basic logical structure in Puppet. Supports a list of # tests and statement arrays. class CaseStatement < AST::Branch attr_accessor :test, :options, :default associates_doc # Short-curcuit evaluation. Return the value of the statements for # the first option that matches. def evaluate(scope) value = @test.safeevaluate(scope) - sensitive = Puppet[:casesensitive] - value = value.downcase if ! sensitive and value.respond_to?(:downcase) retvalue = nil found = false # Iterate across the options looking for a match. default = nil @options.each do |option| option.eachopt do |opt| - return option.safeevaluate(scope) if opt.evaluate_match(value, scope, :file => file, :line => line, :sensitive => sensitive) + return option.safeevaluate(scope) if opt.evaluate_match(value, scope, :file => file, :line => line, :sensitive => Puppet[:casesensitive]) end default = option if option.default? end # Unless we found something, look for the default. return default.safeevaluate(scope) if default Puppet.debug "No true answers and no default" return nil ensure scope.unset_ephemeral_var end def each [@test,@options].each { |child| yield child } end end end diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb index 0f427ef37..4ad1f9ff3 100644 --- a/lib/puppet/parser/ast/leaf.rb +++ b/lib/puppet/parser/ast/leaf.rb @@ -1,218 +1,218 @@ class Puppet::Parser::AST # The base class for all of the leaves of the parse trees. These # basically just have types and values. Both of these parameters # are simple values, not AST objects. class Leaf < AST attr_accessor :value, :type # Return our value. def evaluate(scope) return @value end # evaluate ourselves, and match def evaluate_match(value, scope, options = {}) obj = self.safeevaluate(scope) - if ! options[:sensitive] && obj.respond_to?(:downcase) - obj = obj.downcase + if options[:sensitive] + obj = obj.downcase if obj.respond_to?(:downcase) + value = value.downcase if value.respond_to?(:downcase) end # "" == undef for case/selector/if - return true if obj == "" and value == :undef - obj == value + obj == value or (obj == "" and value == :undef) end def match(value) @value == value end def to_s return @value.to_s unless @value.nil? end end # The boolean class. True or false. Converts the string it receives # to a Ruby boolean. class Boolean < AST::Leaf # Use the parent method, but then convert to a real boolean. def initialize(hash) super unless @value == true or @value == false raise Puppet::DevError, "'%s' is not a boolean" % @value end @value end def to_s @value ? "true" : "false" end end # The base string class. class String < AST::Leaf # Interpolate the string looking for variables, and then return # the result. def evaluate(scope) return scope.strinterp(@value, file, line) end def to_s "\"#{@value}\"" end end # An uninterpreted string. class FlatString < AST::Leaf def evaluate(scope) return @value end def to_s "\"#{@value}\"" end end # The 'default' option on case statements and selectors. class Default < AST::Leaf; end # Capitalized words; used mostly for type-defaults, but also # get returned by the lexer any other time an unquoted capitalized # word is found. class Type < AST::Leaf; end # Lower-case words. class Name < AST::Leaf; end # double-colon separated class names class ClassName < AST::Leaf; end # undef values; equiv to nil class Undef < AST::Leaf; end # Host names, either fully qualified or just the short name, or even a regex class HostName < AST::Leaf def initialize(hash) super # Note that this is an AST::Regex, not a Regexp @value = @value.to_s.downcase unless @value.is_a?(Regex) if @value =~ /[^-\w.]/ raise Puppet::DevError, "'%s' is not a valid hostname" % @value end end # implementing eql? and hash so that when an HostName is stored # in a hash it has the same hashing properties as the underlying value def eql?(value) value = value.value if value.is_a?(HostName) return @value.eql?(value) end def hash return @value.hash end def to_s @value.to_s end end # A simple variable. This object is only used during interpolation; # the VarDef class is used for assignment. class Variable < Name # Looks up the value of the object in the scope tree (does # not include syntactical constructs, like '$' and '{}'). def evaluate(scope) parsewrap do if (var = scope.lookupvar(@value, false)) == :undefined var = :undef end var end end def to_s "\$#{value}" end end class HashOrArrayAccess < AST::Leaf attr_accessor :variable, :key def evaluate_container(scope) container = variable.respond_to?(:evaluate) ? variable.safeevaluate(scope) : variable (container.is_a?(Hash) or container.is_a?(Array)) ? container : scope.lookupvar(container) end def evaluate_key(scope) key.respond_to?(:evaluate) ? key.safeevaluate(scope) : key end def evaluate(scope) object = evaluate_container(scope) unless object.is_a?(Hash) or object.is_a?(Array) raise Puppet::ParseError, "#{variable} is not an hash or array when accessing it with #{accesskey}" end return object[evaluate_key(scope)] end # Assign value to this hashkey or array index def assign(scope, value) object = evaluate_container(scope) accesskey = evaluate_key(scope) if object.is_a?(Hash) and object.include?(accesskey) raise Puppet::ParseError, "Assigning to the hash '#{variable}' with an existing key '#{accesskey}' is forbidden" end # assign to hash or array object[accesskey] = value end def to_s "\$#{variable.to_s}[#{key.to_s}]" end end class Regex < AST::Leaf def initialize(hash) super @value = Regexp.new(@value) unless @value.is_a?(Regexp) end # we're returning self here to wrap the regexp and to be used in places # where a string would have been used, without modifying any client code. # For instance, in many places we have the following code snippet: # val = @val.safeevaluate(@scope) # if val.match(otherval) # ... # end # this way, we don't have to modify this test specifically for handling # regexes. def evaluate(scope) return self end def evaluate_match(value, scope, options = {}) value = value.is_a?(String) ? value : value.to_s if matched = @value.match(value) scope.ephemeral_from(matched, options[:file], options[:line]) end matched end def match(value) @value.match(value) end def to_s return "/#{@value.source}/" end end end diff --git a/lib/puppet/parser/ast/selector.rb b/lib/puppet/parser/ast/selector.rb index 84bc2a74a..ce834b63b 100644 --- a/lib/puppet/parser/ast/selector.rb +++ b/lib/puppet/parser/ast/selector.rb @@ -1,49 +1,47 @@ require 'puppet/parser/ast/branch' class Puppet::Parser::AST # The inline conditional operator. Unlike CaseStatement, which executes # code, we just return a value. class Selector < AST::Branch attr_accessor :param, :values def each [@param,@values].each { |child| yield child } end # Find the value that corresponds with the test. def evaluate(scope) # Get our parameter. paramvalue = @param.safeevaluate(scope) sensitive = Puppet[:casesensitive] - paramvalue = paramvalue.downcase if not sensitive and paramvalue.respond_to?(:downcase) - default = nil unless @values.instance_of? AST::ASTArray or @values.instance_of? Array @values = [@values] end # Then look for a match in the options. @values.each do |obj| # short circuit asap if we have a match return obj.value.safeevaluate(scope) if obj.param.evaluate_match(paramvalue, scope, :file => file, :line => line, :sensitive => sensitive) # Store the default, in case it's necessary. default = obj if obj.param.is_a?(Default) end # Unless we found something, look for the default. return default.value.safeevaluate(scope) if default self.fail Puppet::ParseError, "No matching value for selector param '%s'" % paramvalue ensure scope.unset_ephemeral_var end def to_s param.to_s + " ? { " + values.collect { |v| v.to_s }.join(', ') + " }" end end end diff --git a/spec/unit/parser/ast/casestatement.rb b/spec/unit/parser/ast/casestatement.rb index 554e295bc..657648e9d 100755 --- a/spec/unit/parser/ast/casestatement.rb +++ b/spec/unit/parser/ast/casestatement.rb @@ -1,143 +1,133 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' describe Puppet::Parser::AST::CaseStatement do before :each do @scope = Puppet::Parser::Scope.new() end describe "when evaluating" do before :each do @test = stub 'test' @test.stubs(:safeevaluate).with(@scope).returns("value") @option1 = stub 'option1', :eachopt => nil, :default? => false @option2 = stub 'option2', :eachopt => nil, :default? => false @options = stub 'options' @options.stubs(:each).multiple_yields(@option1, @option2) @casestmt = Puppet::Parser::AST::CaseStatement.new :test => @test, :options => @options end it "should evaluate test" do @test.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end - it "should downcase the evaluated test value if allowed" do - Puppet.stubs(:[]).with(:casesensitive).returns(false) - value = stub 'test' - @test.stubs(:safeevaluate).with(@scope).returns(value) - - value.expects(:downcase) - - @casestmt.evaluate(@scope) - end - it "should scan each option" do @options.expects(:each).multiple_yields(@option1, @option2) @casestmt.evaluate(@scope) end describe "when scanning options" do before :each do @opval1 = stub_everything 'opval1' @option1.stubs(:eachopt).yields(@opval1) @opval2 = stub_everything 'opval2' @option2.stubs(:eachopt).yields(@opval2) end it "should evaluate each sub-option" do @option1.expects(:eachopt) @option2.expects(:eachopt) @casestmt.evaluate(@scope) end it "should evaluate first matching option" do @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @option2.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should evaluate_match with sensitive parameter" do Puppet.stubs(:[]).with(:casesensitive).returns(true) @opval1.expects(:evaluate_match).with { |*arg| arg[2][:sensitive] == true } @casestmt.evaluate(@scope) end it "should return the first matching evaluated option" do @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @option2.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should evaluate the default option if none matched" do @option1.stubs(:default?).returns(true) @option1.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should return the default evaluated option if none matched" do @option1.stubs(:default?).returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should return nil if nothing matched" do @casestmt.evaluate(@scope).should be_nil end it "should match and set scope ephemeral variables" do @opval1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } @casestmt.evaluate(@scope) end it "should evaluate this regex option if it matches" do @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should return this evaluated regex option if it matches" do @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should unset scope ephemeral variables after option evaluation" do @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @scope.expects(:unset_ephemeral_var) @casestmt.evaluate(@scope) end it "should not leak ephemeral variables even if evaluation fails" do @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).raises @scope.expects(:unset_ephemeral_var) lambda { @casestmt.evaluate(@scope) }.should raise_error end end end end diff --git a/spec/unit/parser/ast/leaf.rb b/spec/unit/parser/ast/leaf.rb index 0c1c5864d..46458605d 100755 --- a/spec/unit/parser/ast/leaf.rb +++ b/spec/unit/parser/ast/leaf.rb @@ -1,354 +1,367 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' describe Puppet::Parser::AST::Leaf do before :each do @scope = stub 'scope' @value = stub 'value' @leaf = Puppet::Parser::AST::Leaf.new(:value => @value) end it "should have a evaluate_match method" do Puppet::Parser::AST::Leaf.new(:value => "value").should respond_to(:evaluate_match) end describe "when evaluate_match is called" do it "should evaluate itself" do @leaf.expects(:safeevaluate).with(@scope) @leaf.evaluate_match("value", @scope) end it "should match values by equality" do @value.stubs(:==).returns(false) @leaf.stubs(:safeevaluate).with(@scope).returns(@value) @value.expects(:==).with("value") @leaf.evaluate_match("value", @scope) end it "should downcase the evaluated value if wanted" do @leaf.stubs(:safeevaluate).with(@scope).returns(@value) @value.expects(:downcase).returns("value") @leaf.evaluate_match("value", @scope, :insensitive => true) end it "should match undef if value is an empty string" do @leaf.stubs(:safeevaluate).with(@scope).returns("") @leaf.evaluate_match(:undef, @scope).should be_true end + + it "should downcase the parameter value if wanted" do + parameter = stub 'parameter' + parameter.expects(:downcase).returns("value") + + @leaf.evaluate_match(parameter, @scope, :insensitive => true) + end end describe "when converting to string" do it "should transform its value to string" do value = stub 'value', :is_a? => true value.expects(:to_s) Puppet::Parser::AST::Leaf.new( :value => value ).to_s end end it "should have a match method" do @leaf.should respond_to(:match) end it "should delegate match to ==" do @value.expects(:==).with("value") @leaf.match("value") end end describe Puppet::Parser::AST::FlatString do describe "when converting to string" do it "should transform its value to a quoted string" do value = stub 'value', :is_a? => true, :to_s => "ab" Puppet::Parser::AST::FlatString.new( :value => value ).to_s.should == "\"ab\"" end end end describe Puppet::Parser::AST::String do describe "when converting to string" do it "should transform its value to a quoted string" do value = stub 'value', :is_a? => true, :to_s => "ab" Puppet::Parser::AST::String.new( :value => value ).to_s.should == "\"ab\"" end end end describe Puppet::Parser::AST::Undef do before :each do @scope = stub 'scope' @undef = Puppet::Parser::AST::Undef.new(:value => :undef) end it "should match undef with undef" do @undef.evaluate_match(:undef, @scope).should be_true end it "should not match undef with an empty string" do @undef.evaluate_match("", @scope).should be_false end end describe Puppet::Parser::AST::HashOrArrayAccess do before :each do @scope = stub 'scope' end describe "when evaluating" do it "should evaluate the variable part if necessary" do @scope.stubs(:lookupvar).with("a").returns(["b"]) variable = stub 'variable', :evaluate => "a" access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => variable, :key => 0 ) variable.expects(:safeevaluate).with(@scope).returns("a") access.evaluate(@scope).should == "b" end it "should evaluate the access key part if necessary" do @scope.stubs(:lookupvar).with("a").returns(["b"]) index = stub 'index', :evaluate => 0 access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => index ) index.expects(:safeevaluate).with(@scope).returns(0) access.evaluate(@scope).should == "b" end it "should be able to return an array member" do @scope.stubs(:lookupvar).with("a").returns(["val1", "val2", "val3"]) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => 1 ) access.evaluate(@scope).should == "val2" end it "should be able to return an hash value" do @scope.stubs(:lookupvar).with("a").returns({ "key1" => "val1", "key2" => "val2", "key3" => "val3" }) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) access.evaluate(@scope).should == "val2" end it "should raise an error if the variable lookup didn't return an hash or an array" do @scope.stubs(:lookupvar).with("a").returns("I'm a string") access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) lambda { access.evaluate(@scope) }.should raise_error end it "should raise an error if the variable wasn't in the scope" do @scope.stubs(:lookupvar).with("a").returns(nil) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) lambda { access.evaluate(@scope) }.should raise_error end it "should return a correct string representation" do access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) access.to_s.should == '$a[key2]' end it "should work with recursive hash access" do @scope.stubs(:lookupvar).with("a").returns({ "key" => { "subkey" => "b" }}) access1 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") access2 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => access1, :key => "subkey") access2.evaluate(@scope).should == 'b' end it "should work with interleaved array and hash access" do @scope.stubs(:lookupvar).with("a").returns({ "key" => [ "a" , "b" ]}) access1 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") access2 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => access1, :key => 1) access2.evaluate(@scope).should == 'b' end end describe "when assigning" do it "should add a new key and value" do scope = Puppet::Parser::Scope.new scope.setvar("a", { 'a' => 'b' }) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "b") access.assign(scope, "c" ) scope.lookupvar("a").should be_include("b") end it "should raise an error when trying to overwrite an hash value" do @scope.stubs(:lookupvar).with("a").returns({ "key" => [ "a" , "b" ]}) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") lambda { access.assign(@scope, "test") }.should raise_error end end end describe Puppet::Parser::AST::Regex do before :each do @scope = stub 'scope' end describe "when initializing" do it "should create a Regexp with its content when value is not a Regexp" do Regexp.expects(:new).with("/ab/") Puppet::Parser::AST::Regex.new :value => "/ab/" end it "should not create a Regexp with its content when value is a Regexp" do value = Regexp.new("/ab/") Regexp.expects(:new).with("/ab/").never Puppet::Parser::AST::Regex.new :value => value end end describe "when evaluating" do it "should return self" do val = Puppet::Parser::AST::Regex.new :value => "/ab/" val.evaluate(@scope).should === val end end describe "when evaluate_match" do before :each do @value = stub 'regex' @value.stubs(:match).with("value").returns(true) Regexp.stubs(:new).returns(@value) @regex = Puppet::Parser::AST::Regex.new :value => "/ab/" end it "should issue the regexp match" do @value.expects(:match).with("value") @regex.evaluate_match("value", @scope) end + it "should not downcase the paramater value" do + @value.expects(:match).with("VaLuE") + + @regex.evaluate_match("VaLuE", @scope) + end + it "should set ephemeral scope vars if there is a match" do @scope.expects(:ephemeral_from).with(true, nil, nil) @regex.evaluate_match("value", @scope) end it "should return the match to the caller" do @value.stubs(:match).with("value").returns(:match) @scope.stubs(:ephemeral_from) @regex.evaluate_match("value", @scope) end end it "should return the regex source with to_s" do regex = stub 'regex' Regexp.stubs(:new).returns(regex) val = Puppet::Parser::AST::Regex.new :value => "/ab/" regex.expects(:source) val.to_s end it "should delegate match to the underlying regexp match method" do regex = Regexp.new("/ab/") val = Puppet::Parser::AST::Regex.new :value => regex regex.expects(:match).with("value") val.match("value") end end describe Puppet::Parser::AST::Variable do before :each do @scope = stub 'scope' @var = Puppet::Parser::AST::Variable.new(:value => "myvar") end it "should lookup the variable in scope" do @scope.expects(:lookupvar).with("myvar", false).returns(:myvalue) @var.safeevaluate(@scope).should == :myvalue end it "should return undef if the variable wasn't set" do @scope.expects(:lookupvar).with("myvar", false).returns(:undefined) @var.safeevaluate(@scope).should == :undef end describe "when converting to string" do it "should transform its value to a variable" do value = stub 'value', :is_a? => true, :to_s => "myvar" Puppet::Parser::AST::Variable.new( :value => value ).to_s.should == "\$myvar" end end end describe Puppet::Parser::AST::HostName do before :each do @scope = stub 'scope' @value = stub 'value', :=~ => false @value.stubs(:to_s).returns(@value) @value.stubs(:downcase).returns(@value) @host = Puppet::Parser::AST::HostName.new( :value => @value) end it "should raise an error if hostname is not valid" do lambda { Puppet::Parser::AST::HostName.new( :value => "not an hostname!" ) }.should raise_error end it "should not raise an error if hostname is a regex" do lambda { Puppet::Parser::AST::HostName.new( :value => Puppet::Parser::AST::Regex.new(:value => "/test/") ) }.should_not raise_error end it "should stringify the value" do value = stub 'value', :=~ => false value.expects(:to_s).returns("test") Puppet::Parser::AST::HostName.new(:value => value) end it "should downcase the value" do value = stub 'value', :=~ => false value.stubs(:to_s).returns("UPCASED") host = Puppet::Parser::AST::HostName.new(:value => value) host.value == "upcased" end it "should evaluate to its value" do @host.evaluate(@scope).should == @value end it "should delegate eql? to the underlying value if it is an HostName" do @value.expects(:eql?).with("value") @host.eql?("value") end it "should delegate eql? to the underlying value if it is not an HostName" do value = stub 'compared', :is_a? => true, :value => "value" @value.expects(:eql?).with("value") @host.eql?(value) end it "should delegate hash to the underlying value" do @value.expects(:hash) @host.hash end end diff --git a/spec/unit/parser/ast/selector.rb b/spec/unit/parser/ast/selector.rb index 2ba83ad7b..f9a1efe6c 100755 --- a/spec/unit/parser/ast/selector.rb +++ b/spec/unit/parser/ast/selector.rb @@ -1,163 +1,153 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' describe Puppet::Parser::AST::Selector do before :each do @scope = Puppet::Parser::Scope.new() end describe "when evaluating" do before :each do @param = stub 'param' @param.stubs(:safeevaluate).with(@scope).returns("value") @value1 = stub 'value1' @param1 = stub_everything 'param1' @param1.stubs(:safeevaluate).with(@scope).returns(@param1) @param1.stubs(:respond_to?).with(:downcase).returns(false) @value1.stubs(:param).returns(@param1) @value1.stubs(:value).returns(@value1) @value2 = stub 'value2' @param2 = stub_everything 'param2' @param2.stubs(:safeevaluate).with(@scope).returns(@param2) @param2.stubs(:respond_to?).with(:downcase).returns(false) @value2.stubs(:param).returns(@param2) @value2.stubs(:value).returns(@value2) @values = stub 'values', :instance_of? => true @values.stubs(:each).multiple_yields(@value1, @value2) @selector = Puppet::Parser::AST::Selector.new :param => @param, :values => @values @selector.stubs(:fail) end it "should evaluate param" do @param.expects(:safeevaluate).with(@scope) @selector.evaluate(@scope) end - it "should downcase the evaluated param value if allowed" do - Puppet.stubs(:[]).with(:casesensitive).returns(false) - value = stub 'param' - @param.stubs(:safeevaluate).with(@scope).returns(value) - - value.expects(:downcase) - - @selector.evaluate(@scope) - end - it "should scan each option" do @values.expects(:each).multiple_yields(@value1, @value2) @selector.evaluate(@scope) end describe "when scanning values" do it "should evaluate first matching option" do @param2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @value2.expects(:safeevaluate).with(@scope) @selector.evaluate(@scope) end it "should return the first matching evaluated option" do @param2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @value2.stubs(:safeevaluate).with(@scope).returns(:result) @selector.evaluate(@scope).should == :result end it "should evaluate the default option if none matched" do @param1.stubs(:is_a?).with(Puppet::Parser::AST::Default).returns(true) @value1.expects(:safeevaluate).with(@scope).returns(@param1) @selector.evaluate(@scope) end it "should return the default evaluated option if none matched" do result = stub 'result' @param1.stubs(:is_a?).with(Puppet::Parser::AST::Default).returns(true) @value1.stubs(:safeevaluate).returns(result) @selector.evaluate(@scope).should == result end it "should return nil if nothing matched" do @selector.evaluate(@scope).should be_nil end it "should delegate matching to evaluate_match" do @param1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } @selector.evaluate(@scope) end it "should transmit the sensitive parameter to evaluate_match" do Puppet.stubs(:[]).with(:casesensitive).returns(:sensitive) @param1.expects(:evaluate_match).with { |*arg| arg[2][:sensitive] == :sensitive } @selector.evaluate(@scope) end it "should transmit the AST file and line to evaluate_match" do @selector.file = :file @selector.line = :line @param1.expects(:evaluate_match).with { |*arg| arg[2][:file] == :file and arg[2][:line] == :line } @selector.evaluate(@scope) end it "should evaluate the matching param" do @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @value1.expects(:safeevaluate).with(@scope) @selector.evaluate(@scope) end it "should return this evaluated option if it matches" do @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @value1.stubs(:safeevaluate).with(@scope).returns(:result) @selector.evaluate(@scope).should == :result end it "should unset scope ephemeral variables after option evaluation" do @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @value1.stubs(:safeevaluate).with(@scope).returns(:result) @scope.expects(:unset_ephemeral_var) @selector.evaluate(@scope) end it "should not leak ephemeral variables even if evaluation fails" do @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @value1.stubs(:safeevaluate).with(@scope).raises @scope.expects(:unset_ephemeral_var) lambda { @selector.evaluate(@scope) }.should raise_error end it "should fail if there is no default" do @selector.expects(:fail) @selector.evaluate(@scope) end end end describe "when converting to string" do it "should produce a string version of this selector" do values = Puppet::Parser::AST::ASTArray.new :children => [ Puppet::Parser::AST::ResourceParam.new(:param => "type", :value => "value", :add => false) ] param = Puppet::Parser::AST::Variable.new :value => "myvar" selector = Puppet::Parser::AST::Selector.new :param => param, :values => values selector.to_s.should == "$myvar ? { type => value }" end end end