summaryrefslogtreecommitdiff
path: root/spec/unit/functions
diff options
context:
space:
mode:
authorStig Sandbeck Mathisen <ssm@debian.org>2014-09-07 10:14:36 +0200
committerStig Sandbeck Mathisen <ssm@debian.org>2014-09-07 10:14:36 +0200
commitd4b83be375ac1dead058e091191ee7c7b7c24c8a (patch)
treedc825687392ae3068de5b764be60c53122d9e02a /spec/unit/functions
parent229cbb976fe0f70f5f30548b83517b415840f9bb (diff)
parent1681684857c6e39d60d87b0b3520d8783977ceff (diff)
downloadpuppet-upstream/3.7.0.tar.gz
Imported Upstream version 3.7.0upstream/3.7.0
Diffstat (limited to 'spec/unit/functions')
-rw-r--r--spec/unit/functions/assert_type_spec.rb25
-rw-r--r--spec/unit/functions/each_spec.rb111
-rw-r--r--spec/unit/functions/epp_spec.rb155
-rw-r--r--spec/unit/functions/filter_spec.rb131
-rw-r--r--spec/unit/functions/inline_epp_spec.rb97
-rw-r--r--spec/unit/functions/map_spec.rb169
-rw-r--r--spec/unit/functions/match_spec.rb57
-rw-r--r--spec/unit/functions/reduce_spec.rb96
-rw-r--r--spec/unit/functions/slice_spec.rb148
-rw-r--r--spec/unit/functions/with_spec.rb35
10 files changed, 1021 insertions, 3 deletions
diff --git a/spec/unit/functions/assert_type_spec.rb b/spec/unit/functions/assert_type_spec.rb
index d47f47e30..13b353401 100644
--- a/spec/unit/functions/assert_type_spec.rb
+++ b/spec/unit/functions/assert_type_spec.rb
@@ -37,8 +37,8 @@ describe 'the assert_type function' do
end.to raise_error(ArgumentError, Regexp.new(Regexp.escape(
"function 'assert_type' called with mis-matched arguments
expected one of:
- assert_type(Type type, Optional[Object] value) - arg count {2}
- assert_type(String type_string, Optional[Object] value) - arg count {2}
+ assert_type(Type type, Any value, Callable[Type, Type] block {0,1}) - arg count {2,3}
+ assert_type(String type_string, Any value, Callable[Type, Type] block {0,1}) - arg count {2,3}
actual:
assert_type(Integer, Integer) - arg count {2}")))
end
@@ -46,7 +46,13 @@ actual:
it 'allows the second arg to be undef/nil)' do
expect do
func.call({}, optional(String), nil)
- end.to_not raise_error(ArgumentError)
+ end.to_not raise_error
+ end
+
+ it 'can be called with a callable that receives a specific type' do
+ expected, actual = func.call({}, optional(String), 1, create_callable_2_args_unit)
+ expect(expected.to_s).to eql('Optional[String]')
+ expect(actual.to_s).to eql('Integer[1, 1]')
end
def optional(type_ref)
@@ -56,4 +62,17 @@ actual:
def type(type_ref)
Puppet::Pops::Types::TypeFactory.type_of(type_ref)
end
+
+ def create_callable_2_args_unit()
+ Puppet::Functions.create_function(:func) do
+ dispatch :func do
+ param 'Type', 'expected'
+ param 'Type', 'actual'
+ end
+
+ def func(expected, actual)
+ [expected, actual]
+ end
+ end.new({}, nil)
+ end
end
diff --git a/spec/unit/functions/each_spec.rb b/spec/unit/functions/each_spec.rb
new file mode 100644
index 000000000..d6651cf5a
--- /dev/null
+++ b/spec/unit/functions/each_spec.rb
@@ -0,0 +1,111 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+
+require 'shared_behaviours/iterative_functions'
+
+describe 'the each method' do
+ include PuppetSpec::Compiler
+
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+
+ context "should be callable as" do
+ it 'each on an array selecting each value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $a.each |$v| {
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+ end
+
+ it 'each on an array selecting each value - function call style' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ each ($a) |$index, $v| {
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+ end
+
+ it 'each on an array with index' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [present, absent, present]
+ $a.each |$k,$v| {
+ file { "/file_${$k+1}": ensure => $v }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
+ catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+ end
+
+ it 'each on a hash selecting entries' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>'present','b'=>'absent','c'=>'present'}
+ $a.each |$e| {
+ file { "/file_${e[0]}": ensure => $e[1] }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
+ catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+ end
+
+ it 'each on a hash selecting key and value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>present,'b'=>absent,'c'=>present}
+ $a.each |$k, $v| {
+ file { "/file_$k": ensure => $v }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
+ catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+ end
+
+ it 'each on a hash selecting key and value (using captures-last parameter)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>present,'b'=>absent,'c'=>present}
+ $a.each |*$kv| {
+ file { "/file_${kv[0]}": ensure => $kv[1] }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
+ catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+ end
+ end
+
+ context "should produce receiver" do
+ it 'each checking produced value using single expression' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1, 3, 2]
+ $b = $a.each |$x| { "unwanted" }
+ file { "/file_${b[1]}":
+ ensure => present
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+ end
+
+ end
+ it_should_behave_like 'all iterative functions argument checks', 'each'
+ it_should_behave_like 'all iterative functions hash handling', 'each'
+
+end
diff --git a/spec/unit/functions/epp_spec.rb b/spec/unit/functions/epp_spec.rb
new file mode 100644
index 000000000..382fd9548
--- /dev/null
+++ b/spec/unit/functions/epp_spec.rb
@@ -0,0 +1,155 @@
+
+require 'spec_helper'
+
+describe "the epp function" do
+ include PuppetSpec::Files
+
+ before :all do
+ Puppet::Parser::Functions.autoloader.loadall
+ end
+
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+
+ let :node do Puppet::Node.new('localhost') end
+ let :compiler do Puppet::Parser::Compiler.new(node) end
+ let :scope do Puppet::Parser::Scope.new(compiler) end
+
+ context "when accessing scope variables as $ variables" do
+ it "looks up the value from the scope" do
+ scope["what"] = "are belong"
+ eval_template("all your base <%= $what %> to us").should == "all your base are belong to us"
+ end
+
+ it "get nil accessing a variable that does not exist" do
+ eval_template("<%= $kryptonite == undef %>").should == "true"
+ end
+
+ it "get nil accessing a variable that is undef" do
+ scope['undef_var'] = nil
+ eval_template("<%= $undef_var == undef %>").should == "true"
+ end
+
+ it "gets shadowed variable if args are given" do
+ scope['phantom'] = 'of the opera'
+ eval_template_with_args("<%= $phantom == dragos %>", 'phantom' => 'dragos').should == "true"
+ end
+
+ it "can use values from the enclosing scope for defaults" do
+ scope['phantom'] = 'of the opera'
+ eval_template("<%- |$phantom = $phantom| -%><%= $phantom %>").should == "of the opera"
+ end
+
+ it "uses the default value if the given value is undef/nil" do
+ eval_template_with_args("<%- |$phantom = 'inside your mind'| -%><%= $phantom %>", 'phantom' => nil).should == "inside your mind"
+ end
+
+ it "gets shadowed variable if args are given and parameters are specified" do
+ scope['x'] = 'wrong one'
+ eval_template_with_args("<%- |$x| -%><%= $x == correct %>", 'x' => 'correct').should == "true"
+ end
+
+ it "raises an error if required variable is not given" do
+ scope['x'] = 'wrong one'
+ expect do
+ eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'y' => 'correct')
+ end.to raise_error(/no value given for required parameters x/)
+ end
+
+ it "raises an error if too many arguments are given" do
+ scope['x'] = 'wrong one'
+ expect do
+ eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus')
+ end.to raise_error(/Too many arguments: 2 for 1/)
+ end
+ end
+
+ context "when given an empty template" do
+ it "allows the template file to be empty" do
+ expect(eval_template("")).to eq("")
+ end
+
+ it "allows the template to have empty body after parameters" do
+ expect(eval_template_with_args("<%-|$x|%>", 'x'=>1)).to eq("")
+ end
+ end
+
+ context "when using typed parameters" do
+ it "allows a passed value that matches the parameter's type" do
+ expect(eval_template_with_args("<%-|String $x|-%><%= $x == correct %>", 'x' => 'correct')).to eq("true")
+ end
+
+ it "does not allow slurped parameters" do
+ expect do
+ eval_template_with_args("<%-|*$x|-%><%= $x %>", 'x' => 'incorrect')
+ end.to raise_error(/'captures rest' - not supported in an Epp Template/)
+ end
+
+ it "raises an error when the passed value does not match the parameter's type" do
+ expect do
+ eval_template_with_args("<%-|Integer $x|-%><%= $x %>", 'x' => 'incorrect')
+ end.to raise_error(/expected.*Integer.*actual.*String/m)
+ end
+
+ it "raises an error when the default value does not match the parameter's type" do
+ expect do
+ eval_template("<%-|Integer $x = 'nope'|-%><%= $x %>")
+ end.to raise_error(/expected.*Integer.*actual.*String/m)
+ end
+
+ it "allows an parameter to default to undef" do
+ expect(eval_template("<%-|Optional[Integer] $x = undef|-%><%= $x == undef %>")).to eq("true")
+ end
+ end
+
+
+ # although never a problem with epp
+ it "is not interfered with by having a variable named 'string' (#14093)" do
+ scope['string'] = "this output should not be seen"
+ eval_template("some text that is static").should == "some text that is static"
+ end
+
+ it "has access to a variable named 'string' (#14093)" do
+ scope['string'] = "the string value"
+ eval_template("string was: <%= $string %>").should == "string was: the string value"
+ end
+
+ describe 'when loading from modules' do
+ include PuppetSpec::Files
+ it 'an epp template is found' do
+ modules_dir = dir_containing('modules', {
+ 'testmodule' => {
+ 'templates' => {
+ 'the_x.epp' => 'The x is <%= $x %>'
+ }
+ }})
+ Puppet.override({:current_environment => (env = Puppet::Node::Environment.create(:testload, [ modules_dir ]))}, "test") do
+ node.environment = env
+ expect(epp_function.call(scope, 'testmodule/the_x.epp', { 'x' => '3'} )).to eql("The x is 3")
+ end
+ end
+ end
+
+ def eval_template_with_args(content, args_hash)
+ file_path = tmpdir('epp_spec_content')
+ filename = File.join(file_path, "template.epp")
+ File.open(filename, "w+") { |f| f.write(content) }
+
+ Puppet::Parser::Files.stubs(:find_template).returns(filename)
+ epp_function.call(scope, 'template', args_hash)
+ end
+
+ def eval_template(content)
+ file_path = tmpdir('epp_spec_content')
+ filename = File.join(file_path, "template.epp")
+ File.open(filename, "w+") { |f| f.write(content) }
+
+ Puppet::Parser::Files.stubs(:find_template).returns(filename)
+ epp_function.call(scope, 'template')
+ end
+
+ def epp_function()
+ epp_func = scope.compiler.loaders.public_environment_loader.load(:function, 'epp')
+ end
+end
diff --git a/spec/unit/functions/filter_spec.rb b/spec/unit/functions/filter_spec.rb
new file mode 100644
index 000000000..e904c6751
--- /dev/null
+++ b/spec/unit/functions/filter_spec.rb
@@ -0,0 +1,131 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+
+require 'shared_behaviours/iterative_functions'
+
+describe 'the filter method' do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+
+ it 'should filter on an array (all berries)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = ['strawberry','blueberry','orange']
+ $a.filter |$x|{ $x =~ /berry$/}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present')
+ end
+
+ it 'should filter on enumerable type (Integer)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = Integer[1,10]
+ $a.filter |$x|{ $x % 3 == 0}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_9]").with_parameter(:ensure, 'present')
+ end
+
+ it 'should filter on enumerable type (Integer) using two args index/value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = Integer[10,18]
+ $a.filter |$i, $x|{ $i % 3 == 0}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_10]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_13]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_16]").with_parameter(:ensure, 'present')
+ end
+
+ it 'should produce an array when acting on an array' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = ['strawberry','blueberry','orange']
+ $b = $a.filter |$x|{ $x =~ /berry$/}
+ file { "/file_${b[0]}": ensure => present }
+ file { "/file_${b[1]}": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present')
+ end
+
+ it 'can filter array using index and value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = ['strawberry','blueberry','orange']
+ $b = $a.filter |$index, $x|{ $index == 0 or $index ==2}
+ file { "/file_${b[0]}": ensure => present }
+ file { "/file_${b[1]}": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_orange]").with_parameter(:ensure, 'present')
+ end
+
+ it 'can filter array using index and value (using captures-rest)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = ['strawberry','blueberry','orange']
+ $b = $a.filter |*$ix|{ $ix[0] == 0 or $ix[0] ==2}
+ file { "/file_${b[0]}": ensure => present }
+ file { "/file_${b[1]}": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_orange]").with_parameter(:ensure, 'present')
+ end
+
+ it 'filters on a hash (all berries) by key' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'}
+ $a.filter |$x|{ $x[0] =~ /berry$/}.each |$v|{
+ file { "/file_${v[0]}": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present')
+ end
+
+ it 'should produce a hash when acting on a hash' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'}
+ $b = $a.filter |$x|{ $x[0] =~ /berry$/}
+ file { "/file_${b['strawberry']}": ensure => present }
+ file { "/file_${b['blueberry']}": ensure => present }
+ file { "/file_${b['orange']}": ensure => present }
+
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_red]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blue]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_]").with_parameter(:ensure, 'present')
+ end
+
+ it 'filters on a hash (all berries) by value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'strawb'=>'red berry','blueb'=>'blue berry','orange'=>'orange fruit'}
+ $a.filter |$x|{ $x[1] =~ /berry$/}.each |$v|{
+ file { "/file_${v[0]}": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_strawb]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blueb]").with_parameter(:ensure, 'present')
+ end
+
+ it_should_behave_like 'all iterative functions argument checks', 'filter'
+ it_should_behave_like 'all iterative functions hash handling', 'filter'
+end
diff --git a/spec/unit/functions/inline_epp_spec.rb b/spec/unit/functions/inline_epp_spec.rb
new file mode 100644
index 000000000..36328c2dc
--- /dev/null
+++ b/spec/unit/functions/inline_epp_spec.rb
@@ -0,0 +1,97 @@
+
+require 'spec_helper'
+
+describe "the inline_epp function" do
+ include PuppetSpec::Files
+
+ before :all do
+ Puppet::Parser::Functions.autoloader.loadall
+ end
+
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+
+ let :node do Puppet::Node.new('localhost') end
+ let :compiler do Puppet::Parser::Compiler.new(node) end
+ let :scope do Puppet::Parser::Scope.new(compiler) end
+
+ context "when accessing scope variables as $ variables" do
+ it "looks up the value from the scope" do
+ scope["what"] = "are belong"
+ eval_template("all your base <%= $what %> to us").should == "all your base are belong to us"
+ end
+
+ it "get nil accessing a variable that does not exist" do
+ eval_template("<%= $kryptonite == undef %>").should == "true"
+ end
+
+ it "get nil accessing a variable that is undef" do
+ scope['undef_var'] = :undef
+ eval_template("<%= $undef_var == undef %>").should == "true"
+ end
+
+ it "gets shadowed variable if args are given" do
+ scope['phantom'] = 'of the opera'
+ eval_template_with_args("<%= $phantom == dragos %>", 'phantom' => 'dragos').should == "true"
+ end
+
+ it "gets shadowed variable if args are given and parameters are specified" do
+ scope['x'] = 'wrong one'
+ eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct').should == "true"
+ end
+
+ it "raises an error if required variable is not given" do
+ scope['x'] = 'wrong one'
+ expect {
+ eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'y' => 'correct')
+ }.to raise_error(/no value given for required parameters x/)
+ end
+
+ it "raises an error if too many arguments are given" do
+ scope['x'] = 'wrong one'
+ expect {
+ eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus')
+ }.to raise_error(/Too many arguments: 2 for 1/)
+ end
+ end
+
+ context "when given an empty template" do
+ it "allows the template file to be empty" do
+ expect(eval_template("")).to eq("")
+ end
+
+ it "allows the template to have empty body after parameters" do
+ expect(eval_template_with_args("<%-|$x|%>", 'x'=>1)).to eq("")
+ end
+ end
+
+ it "renders a block expression" do
+ eval_template_with_args("<%= { $y = $x $x + 1} %>", 'x' => 2).should == "3"
+ end
+
+ # although never a problem with epp
+ it "is not interfered with by having a variable named 'string' (#14093)" do
+ scope['string'] = "this output should not be seen"
+ eval_template("some text that is static").should == "some text that is static"
+ end
+
+ it "has access to a variable named 'string' (#14093)" do
+ scope['string'] = "the string value"
+ eval_template("string was: <%= $string %>").should == "string was: the string value"
+ end
+
+
+ def eval_template_with_args(content, args_hash)
+ epp_function.call(scope, content, args_hash)
+ end
+
+ def eval_template(content)
+ epp_function.call(scope, content)
+ end
+
+ def epp_function()
+ epp_func = scope.compiler.loaders.public_environment_loader.load(:function, 'inline_epp')
+ end
+
+end
diff --git a/spec/unit/functions/map_spec.rb b/spec/unit/functions/map_spec.rb
new file mode 100644
index 000000000..e1b09cf24
--- /dev/null
+++ b/spec/unit/functions/map_spec.rb
@@ -0,0 +1,169 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+
+require 'shared_behaviours/iterative_functions'
+
+describe 'the map method' do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ before :each do
+ Puppet[:parser] = "future"
+ end
+
+ context "using future parser" do
+ it 'map on an array (multiplying each value by 2)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $a.map |$x|{ $x*2}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_4]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on an enumerable type (multiplying each value by 2)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = Integer[1,3]
+ $a.map |$x|{ $x*2}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_4]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on an integer (multiply each by 3)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ 3.map |$x|{ $x*3}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_0]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on a string' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {a=>x, b=>y}
+ "ab".map |$x|{$a[$x]}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_x]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_y]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on an array (multiplying value by 10 in even index position)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $a.map |$i, $x|{ if $i % 2 == 0 {$x} else {$x*10}}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_20]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on a hash selecting keys' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$x|{ $x[0]}.each |$k|{
+ file { "/file_$k": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on a hash selecting keys - using two block parameters' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$k,$v|{ file { "/file_$k": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on a hash using captures-last parameter' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>present,'b'=>absent,'c'=>present}
+ $a.map |*$kv|{ file { "/file_${kv[0]}": ensure => $kv[1] } }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present')
+ end
+
+ it 'each on a hash selecting value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$x|{ $x[1]}.each |$k|{ file { "/file_$k": ensure => present } }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'each on a hash selecting value - using two block parameters' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$k,$v|{ file { "/file_$v": ensure => present } }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ context "handles data type corner cases" do
+ it "map gets values that are false" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [false,false]
+ $a.map |$x| { $x }.each |$i, $v| {
+ file { "/file_$i.$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_0.false]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_1.false]").with_parameter(:ensure, 'present')
+ end
+
+ it "map gets values that are nil" do
+ Puppet::Parser::Functions.newfunction(:nil_array, :type => :rvalue) do |args|
+ [nil]
+ end
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = nil_array()
+ $a.map |$x| { $x }.each |$i, $v| {
+ file { "/file_$i.$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_0.]").with_parameter(:ensure, 'present')
+ end
+ end
+
+ it_should_behave_like 'all iterative functions argument checks', 'map'
+ it_should_behave_like 'all iterative functions hash handling', 'map'
+ end
+end
diff --git a/spec/unit/functions/match_spec.rb b/spec/unit/functions/match_spec.rb
new file mode 100644
index 000000000..f4e2e383b
--- /dev/null
+++ b/spec/unit/functions/match_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+require 'puppet/pops'
+require 'puppet/loaders'
+
+describe 'the match function' do
+
+ before(:all) do
+ loaders = Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))
+ Puppet.push_context({:loaders => loaders}, "test-examples")
+ end
+
+ after(:all) do
+ Puppet::Pops::Loaders.clear
+ Puppet::pop_context()
+ end
+
+ let(:func) do
+ Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'match')
+ end
+
+ let(:type_parser) { Puppet::Pops::Types::TypeParser.new }
+
+
+ it 'matches string and regular expression without captures' do
+ expect(func.call({}, 'abc123', /[a-z]+[1-9]+/)).to eql(['abc123'])
+ end
+
+ it 'matches string and regular expression with captures' do
+ expect(func.call({}, 'abc123', /([a-z]+)([1-9]+)/)).to eql(['abc123', 'abc', '123'])
+ end
+
+ it 'produces nil if match is not found' do
+ expect(func.call({}, 'abc123', /([x]+)([6]+)/)).to be_nil
+ end
+
+ [ 'Pattern[/([a-z]+)([1-9]+)/]', # regexp
+ 'Pattern["([a-z]+)([1-9]+)"]', # string
+ 'Regexp[/([a-z]+)([1-9]+)/]', # regexp type
+ 'Pattern[/x9/, /([a-z]+)([1-9]+)/]', # regexp, first found matches
+ ].each do |pattern|
+ it "matches string and type #{pattern} with captures" do
+ expect(func.call({}, 'abc123', type(pattern))).to eql(['abc123', 'abc', '123'])
+ end
+ end
+
+ it 'matches an array of strings and yields a map of the result' do
+ expect(func.call({}, ['abc123', '2a', 'xyz2'], /([a-z]+)[1-9]+/)).to eql([['abc123', 'abc'], nil, ['xyz2', 'xyz']])
+ end
+
+ it 'raises error if Regexp type without regexp is used' do
+ expect{func.call({}, 'abc123', type('Regexp'))}.to raise_error(ArgumentError, /Given Regexp Type has no regular expression/)
+ end
+
+ def type(s)
+ Puppet::Pops::Types::TypeParser.new.parse(s)
+ end
+end
diff --git a/spec/unit/functions/reduce_spec.rb b/spec/unit/functions/reduce_spec.rb
new file mode 100644
index 000000000..032f6ccc4
--- /dev/null
+++ b/spec/unit/functions/reduce_spec.rb
@@ -0,0 +1,96 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+require 'shared_behaviours/iterative_functions'
+
+describe 'the reduce method' do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ before :all do
+ # enable switching back
+ @saved_parser = Puppet[:parser]
+ # These tests only work with future parser
+ end
+ after :all do
+ # switch back to original
+ Puppet[:parser] = @saved_parser
+ end
+
+ before :each do
+ node = Puppet::Node.new("floppy", :environment => 'production')
+ @compiler = Puppet::Parser::Compiler.new(node)
+ @scope = Puppet::Parser::Scope.new(@compiler)
+ @topscope = @scope.compiler.topscope
+ @scope.parent = @topscope
+ Puppet[:parser] = 'future'
+ end
+
+ context "should be callable as" do
+ it 'reduce on an array' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $b = $a.reduce |$memo, $x| { $memo + $x }
+ file { "/file_$b": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'reduce on an array with captures rest in lambda' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $b = $a.reduce |*$mx| { $mx[0] + $mx[1] }
+ file { "/file_$b": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'reduce on enumerable type' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = Integer[1,3]
+ $b = $a.reduce |$memo, $x| { $memo + $x }
+ file { "/file_$b": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'reduce on an array with start value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $b = $a.reduce(4) |$memo, $x| { $memo + $x }
+ file { "/file_$b": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_10]").with_parameter(:ensure, 'present')
+ end
+
+ it 'reduce on a hash' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {a=>1, b=>2, c=>3}
+ $start = [ignored, 4]
+ $b = $a.reduce |$memo, $x| {['sum', $memo[1] + $x[1]] }
+ file { "/file_${$b[0]}_${$b[1]}": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_sum_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'reduce on a hash with start value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {a=>1, b=>2, c=>3}
+ $start = ['ignored', 4]
+ $b = $a.reduce($start) |$memo, $x| { ['sum', $memo[1] + $x[1]] }
+ file { "/file_${$b[0]}_${$b[1]}": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_sum_10]").with_parameter(:ensure, 'present')
+ end
+ end
+
+ it_should_behave_like 'all iterative functions argument checks', 'reduce'
+
+end
diff --git a/spec/unit/functions/slice_spec.rb b/spec/unit/functions/slice_spec.rb
new file mode 100644
index 000000000..945cae5c7
--- /dev/null
+++ b/spec/unit/functions/slice_spec.rb
@@ -0,0 +1,148 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+
+describe 'methods' do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ before :all do
+ # enable switching back
+ @saved_parser = Puppet[:parser]
+ # These tests only work with future parser
+ Puppet[:parser] = 'future'
+ end
+ after :all do
+ # switch back to original
+ Puppet[:parser] = @saved_parser
+ end
+
+ before :each do
+ node = Puppet::Node.new("floppy", :environment => 'production')
+ @compiler = Puppet::Parser::Compiler.new(node)
+ @scope = Puppet::Parser::Scope.new(@compiler)
+ @topscope = @scope.compiler.topscope
+ @scope.parent = @topscope
+ Puppet[:parser] = 'future'
+ end
+
+ context "should be callable on array as" do
+
+ it 'slice with explicit parameters' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1, present, 2, absent, 3, present]
+ $a.slice(2) |$k,$v| {
+ file { "/file_${$k}": ensure => $v }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'slice with captures last' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1, present, 2, absent, 3, present]
+ $a.slice(2) |*$kv| {
+ file { "/file_${$kv[0]}": ensure => $kv[1] }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'slice with one parameter' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1, present, 2, absent, 3, present]
+ $a.slice(2) |$k| {
+ file { "/file_${$k[0]}": ensure => $k[1] }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'slice with shorter last slice' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1, present, 2, present, 3, absent]
+ $a.slice(4) |$a, $b, $c, $d| {
+ file { "/file_$a.$c": ensure => $b }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3.]").with_parameter(:ensure, 'absent')
+ end
+ end
+
+ context "should be callable on hash as" do
+ it 'slice with explicit parameters, missing are empty' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {1=>present, 2=>present, 3=>absent}
+ $a.slice(2) |$a,$b| {
+ file { "/file_${a[0]}.${b[0]}": ensure => $a[1] }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3.]").with_parameter(:ensure, 'absent')
+ end
+ end
+
+ context "should be callable on enumerable types as" do
+ it 'slice with integer range' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = Integer[1,4]
+ $a.slice(2) |$a,$b| {
+ file { "/file_${a}.${b}": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3.4]").with_parameter(:ensure, 'present')
+ end
+
+ it 'slice with integer' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ 4.slice(2) |$a,$b| {
+ file { "/file_${a}.${b}": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_0.1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2.3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'slice with string' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ 'abcd'.slice(2) |$a,$b| {
+ file { "/file_${a}.${b}": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_a.b]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_c.d]").with_parameter(:ensure, 'present')
+ end
+ end
+
+ context "when called without a block" do
+ it "should produce an array with the result" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1, present, 2, absent, 3, present]
+ $a.slice(2).each |$k| {
+ file { "/file_${$k[0]}": ensure => $k[1] }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+ end
+end
diff --git a/spec/unit/functions/with_spec.rb b/spec/unit/functions/with_spec.rb
new file mode 100644
index 000000000..952b14412
--- /dev/null
+++ b/spec/unit/functions/with_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+
+describe 'the with function' do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+
+ it 'calls a lambda passing no arguments' do
+ expect(compile_to_catalog("with() || { notify { testing: } }")).to have_resource('Notify[testing]')
+ end
+
+ it 'calls a lambda passing a single argument' do
+ expect(compile_to_catalog('with(1) |$x| { notify { "testing$x": } }')).to have_resource('Notify[testing1]')
+ end
+
+ it 'calls a lambda passing more than one argument' do
+ expect(compile_to_catalog('with(1, 2) |*$x| { notify { "testing${x[0]}, ${x[1]}": } }')).to have_resource('Notify[testing1, 2]')
+ end
+
+ it 'passes a type reference to a lambda' do
+ expect(compile_to_catalog('notify { test: message => "data" } with(Notify[test]) |$x| { notify { "${x[message]}": } }')).to have_resource('Notify[data]')
+ end
+
+ it 'errors when not given enough arguments for the lambda' do
+ expect do
+ compile_to_catalog('with(1) |$x, $y| { }')
+ end.to raise_error(/Parameter \$y is required but no value was given/m)
+ end
+end