puppet 3.7.4 → 3.7.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +11 -6
  3. data/ext/build_defaults.yaml +2 -2
  4. data/ext/systemd/puppet.service +1 -0
  5. data/lib/hiera/puppet_function.rb +71 -0
  6. data/lib/puppet.rb +12 -0
  7. data/lib/puppet/application/device.rb +22 -5
  8. data/lib/puppet/daemon.rb +13 -4
  9. data/lib/puppet/defaults.rb +27 -4
  10. data/lib/puppet/environments.rb +1 -1
  11. data/lib/puppet/error.rb +4 -0
  12. data/lib/puppet/functions.rb +118 -65
  13. data/lib/puppet/functions/assert_type.rb +5 -5
  14. data/lib/puppet/functions/each.rb +12 -12
  15. data/lib/puppet/functions/epp.rb +3 -4
  16. data/lib/puppet/functions/filter.rb +12 -12
  17. data/lib/puppet/functions/hiera.rb +29 -0
  18. data/lib/puppet/functions/hiera_array.rb +34 -0
  19. data/lib/puppet/functions/hiera_hash.rb +36 -0
  20. data/lib/puppet/functions/hiera_include.rb +50 -0
  21. data/lib/puppet/functions/inline_epp.rb +2 -3
  22. data/lib/puppet/functions/map.rb +12 -12
  23. data/lib/puppet/functions/reduce.rb +6 -6
  24. data/lib/puppet/functions/scanf.rb +3 -3
  25. data/lib/puppet/functions/slice.rb +10 -9
  26. data/lib/puppet/functions/with.rb +3 -4
  27. data/lib/puppet/graph/simple_graph.rb +5 -5
  28. data/lib/puppet/metatype/manager.rb +1 -1
  29. data/lib/puppet/node/environment.rb +1 -1
  30. data/lib/puppet/parser/ast/arithmetic_operator.rb +1 -1
  31. data/lib/puppet/parser/ast/collexpr.rb +1 -1
  32. data/lib/puppet/parser/compiler.rb +3 -3
  33. data/lib/puppet/parser/functions/create_resources.rb +1 -9
  34. data/lib/puppet/parser/functions/defined.rb +1 -1
  35. data/lib/puppet/parser/functions/hiera.rb +20 -11
  36. data/lib/puppet/parser/functions/hiera_array.rb +23 -13
  37. data/lib/puppet/parser/functions/hiera_hash.rb +25 -15
  38. data/lib/puppet/parser/functions/hiera_include.rb +20 -9
  39. data/lib/puppet/parser/functions/lookup.rb +1 -1
  40. data/lib/puppet/parser/functions/realize.rb +1 -1
  41. data/lib/puppet/parser/functions/scanf.rb +21 -12
  42. data/lib/puppet/parser/parser_factory.rb +2 -2
  43. data/lib/puppet/parser/relationship.rb +1 -1
  44. data/lib/puppet/parser/scope.rb +34 -7
  45. data/lib/puppet/pops.rb +2 -0
  46. data/lib/puppet/pops/binder/lookup.rb +24 -7
  47. data/lib/puppet/pops/binder/producers.rb +2 -2
  48. data/lib/puppet/pops/evaluator/closure.rb +1 -1
  49. data/lib/puppet/pops/evaluator/evaluator_impl.rb +109 -17
  50. data/lib/puppet/pops/evaluator/puppet_proc.rb +69 -0
  51. data/lib/puppet/pops/evaluator/runtime3_converter.rb +175 -0
  52. data/lib/puppet/pops/evaluator/runtime3_support.rb +15 -128
  53. data/lib/puppet/pops/functions/dispatch.rb +21 -17
  54. data/lib/puppet/pops/functions/dispatcher.rb +3 -3
  55. data/lib/puppet/pops/functions/function.rb +46 -14
  56. data/lib/puppet/pops/issues.rb +2 -2
  57. data/lib/puppet/pops/model/model_label_provider.rb +1 -1
  58. data/lib/puppet/pops/parser/egrammar.ra +2 -0
  59. data/lib/puppet/pops/parser/eparser.rb +732 -724
  60. data/lib/puppet/pops/parser/heredoc_support.rb +1 -1
  61. data/lib/puppet/pops/parser/lexer2.rb +20 -22
  62. data/lib/puppet/pops/types/class_loader.rb +1 -1
  63. data/lib/puppet/pops/types/type_calculator.rb +104 -37
  64. data/lib/puppet/pops/types/type_factory.rb +1 -1
  65. data/lib/puppet/pops/types/types.rb +4 -1
  66. data/lib/puppet/pops/types/types_meta.rb +2 -2
  67. data/lib/puppet/pops/validation/checker4_0.rb +5 -3
  68. data/lib/puppet/provider/service/systemd.rb +1 -0
  69. data/lib/puppet/provider/yumrepo/inifile.rb +4 -1
  70. data/lib/puppet/resource.rb +3 -2
  71. data/lib/puppet/resource/catalog.rb +3 -2
  72. data/lib/puppet/resource/type.rb +1 -1
  73. data/lib/puppet/settings/environment_conf.rb +12 -4
  74. data/lib/puppet/type/package.rb +23 -13
  75. data/lib/puppet/util/autoload.rb +7 -7
  76. data/lib/puppet/util/errors.rb +4 -2
  77. data/lib/puppet/util/network_device/config.rb +5 -0
  78. data/lib/puppet/version.rb +1 -1
  79. data/lib/puppetx.rb +2 -2
  80. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/callee.rb +8 -0
  81. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/lib/puppet/parser/functions/callee_ws.rb +8 -0
  82. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/usee/metadata.json +9 -0
  83. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/lib/puppet/functions/user/caller.rb +5 -0
  84. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/lib/puppet/functions/user/caller_ws.rb +12 -0
  85. data/spec/fixtures/unit/pops/loaders/loaders/mix_4x_and_3x_functions/user/metadata.json +9 -0
  86. data/spec/integration/parser/environment_spec.rb +47 -0
  87. data/spec/integration/parser/future_compiler_spec.rb +11 -6
  88. data/spec/unit/application/device_spec.rb +52 -14
  89. data/spec/unit/daemon_spec.rb +0 -2
  90. data/spec/unit/environments_spec.rb +2 -2
  91. data/spec/unit/functions/assert_type_spec.rb +4 -25
  92. data/spec/unit/functions/hiera_spec.rb +127 -0
  93. data/spec/unit/functions/with_spec.rb +9 -4
  94. data/spec/unit/functions4_spec.rb +98 -35
  95. data/spec/unit/hiera/backend/puppet_backend_spec.rb +1 -1
  96. data/spec/unit/parser/functions/create_resources_spec.rb +2 -2
  97. data/spec/unit/parser/functions/defined_spec.rb +5 -0
  98. data/spec/unit/parser/functions/lookup_spec.rb +5 -1
  99. data/spec/unit/parser/functions/scanf_spec.rb +30 -0
  100. data/spec/unit/parser/scope_spec.rb +5 -0
  101. data/spec/unit/pops/binder/injector_spec.rb +1 -1
  102. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +33 -5
  103. data/spec/unit/pops/loaders/loaders_spec.rb +22 -1
  104. data/spec/unit/pops/parser/lexer2_spec.rb +28 -16
  105. data/spec/unit/pops/parser/parse_heredoc_spec.rb +21 -0
  106. data/spec/unit/pops/types/type_calculator_spec.rb +141 -19
  107. data/spec/unit/pops/types/type_factory_spec.rb +2 -2
  108. data/spec/unit/pops/validator/validator_spec.rb +25 -3
  109. data/spec/unit/provider/service/systemd_spec.rb +20 -4
  110. data/spec/unit/provider/user/hpux_spec.rb +1 -1
  111. data/spec/unit/provider/yumrepo/inifile_spec.rb +1 -0
  112. data/spec/unit/settings/environment_conf_spec.rb +12 -1
  113. data/spec/unit/type/package_spec.rb +0 -20
  114. data/spec/unit/util/network_device/config_spec.rb +6 -0
  115. metadata +3422 -3405
@@ -34,7 +34,7 @@ describe Hiera::Backend::Puppet_backend do
34
34
  with_config(:puppet => {:datasource => "rspec"},
35
35
  :hierarchy => ["%{foo}", "common"])
36
36
 
37
- @mockscope.expects(:lookupvar).with("foo").returns(nil)
37
+ @mockscope.expects(:lookupvar).at_least_once.with("foo").returns(nil)
38
38
 
39
39
  @backend.hierarchy(@scope, nil).should == ["rspec::common", "ntp::config::rspec", "ntp::rspec"]
40
40
  end
@@ -63,7 +63,7 @@ describe 'function for dynamically creating resources' do
63
63
  it 'should fail to add non-existing type' do
64
64
  expect do
65
65
  @scope.function_create_resources(['create-resource-foo', { 'foo' => {} }])
66
- end.to raise_error(ArgumentError, /Invalid resource type create-resource-foo/)
66
+ end.to raise_error(/Invalid resource type create-resource-foo/)
67
67
  end
68
68
 
69
69
  it 'should be able to add edges' do
@@ -169,7 +169,7 @@ describe 'function for dynamically creating resources' do
169
169
  compile_to_catalog(<<-MANIFEST)
170
170
  create_resources('class', {'blah'=>{'one'=>'two'}})
171
171
  MANIFEST
172
- end.to raise_error(Puppet::Error, 'Could not find declared class blah at line 1 on node foonode')
172
+ end.to raise_error(/Could not find declared class blah at line 1 on node foonode/)
173
173
  end
174
174
 
175
175
  it 'should be able to add edges' do
@@ -55,6 +55,11 @@ describe "the 'defined' function" do
55
55
  expect(@scope.function_defined(['$x'])).to be_true
56
56
  end
57
57
 
58
+ it "is true when ::variable exists in scope" do
59
+ @compiler.topscope['x'] = 'something'
60
+ expect(@scope.function_defined(['$::x'])).to be_true
61
+ end
62
+
58
63
  it "is true when at least one variable exists in scope" do
59
64
  @scope['x'] = 'something'
60
65
  expect(@scope.function_defined(['$y', '$x', '$z'])).to be_true
@@ -17,6 +17,10 @@ describe "lookup function" do
17
17
  expect do
18
18
  scope.function_lookup([])
19
19
  end.to raise_error(ArgumentError, /Wrong number of arguments/)
20
+
21
+ expect do
22
+ scope.function_lookup([1,2])
23
+ end.to raise_error(Puppet::ParseError, /given 'name' argument, has wrong type/)
20
24
  end
21
25
 
22
26
  it "looks up a value that exists" do
@@ -42,7 +46,7 @@ describe "lookup function" do
42
46
  end
43
47
 
44
48
  it "hiera is called to lookup if value is not bound" do
45
- Puppet::Parser::Scope.any_instance.stubs(:function_hiera).returns('from_hiera')
49
+ Hiera.any_instance.stubs(:lookup).returns('from_hiera')
46
50
  scope = scope_with_injections_from(bound(bind_single("another_value", "something")))
47
51
  expect(scope.function_lookup(['a_value'])).to eq('from_hiera')
48
52
  end
@@ -0,0 +1,30 @@
1
+ #! /usr/bin/env ruby
2
+ require 'spec_helper'
3
+
4
+ describe "the scanf function" do
5
+ before :all do
6
+ Puppet::Parser::Functions.autoloader.loadall
7
+ end
8
+
9
+ let(:node) { Puppet::Node.new('localhost') }
10
+ let(:compiler) { Puppet::Parser::Compiler.new(node) }
11
+ let(:scope) { Puppet::Parser::Scope.new(compiler) }
12
+
13
+ it 'scans a value and returns an array' do
14
+ expect(scope.function_scanf(['42', '%i'])[0] == 42)
15
+ end
16
+
17
+ it 'returns empty array if nothing was scanned' do
18
+ expect(scope.function_scanf(['no', '%i']) == [])
19
+ end
20
+
21
+ it 'produces result up to first unsuccessful scan' do
22
+ expect(scope.function_scanf(['42 no', '%i'])[0] == 42)
23
+ end
24
+
25
+ it 'errors when not given enough arguments' do
26
+ expect do
27
+ scope.function_scanf(['42'])
28
+ end.to raise_error(/.*scanf\(\): Wrong number of arguments given/m)
29
+ end
30
+ end
@@ -281,6 +281,11 @@ describe Puppet::Parser::Scope do
281
281
  it "should raise an error when unknown qualified variable is looked up" do
282
282
  expect { @scope['nowhere::john_doe'] }.to raise_error(/Undefined variable/)
283
283
  end
284
+
285
+ it "should not raise an error when built in variable is looked up" do
286
+ expect { @scope['caller_module_name'] }.to_not raise_error
287
+ expect { @scope['module_name'] }.to_not raise_error
288
+ end
284
289
  end
285
290
  end
286
291
 
@@ -752,7 +752,7 @@ describe 'Injector' do
752
752
  end
753
753
 
754
754
  it "should be possible to post process lookup with a ruby proc" do
755
- transformer = lambda {|scope, value| value + 1 }
755
+ transformer = lambda {|value| value + 1 }
756
756
  bindings.bind.name('an_int').to(42).producer_options( :transformer => transformer)
757
757
  injector(lbinder).lookup(scope, 'an_int').should == 43
758
758
  end
@@ -508,8 +508,14 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
508
508
  Type[Integer] : { yes } }" => 'yes',
509
509
  # supports unfold
510
510
  "case ringo {
511
- *[paul, john, ringo, george] : { 'beatle' } }" => 'beatle',
511
+ *[paul, john, ringo, george] : { 'beatle' } }" => 'beatle',
512
512
 
513
+ "case undef {
514
+ undef : { 'yes' } }" => 'yes',
515
+
516
+ "case undef {
517
+ *undef : { 'no' }
518
+ default :{ 'yes' }}" => 'yes',
513
519
  }.each do |source, result|
514
520
  it "should parse and evaluate the expression '#{source}' to #{result}" do
515
521
  parser.evaluate_string(scope, source, __FILE__).should == result
@@ -526,6 +532,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
526
532
  "'banana' ? { /.*(ana).*/ => $1 }" => 'ana',
527
533
  "[2] ? { Array[String] => yes, Array => yes}" => 'yes',
528
534
  "ringo ? *[paul, john, ringo, george] => 'beatle'" => 'beatle',
535
+ "undef ? undef => 'yes'" => 'yes',
536
+ "undef ? {*undef => 'no', default => 'yes'}" => 'yes',
529
537
  }.each do |source, result|
530
538
  it "should parse and evaluate the expression '#{source}' to #{result}" do
531
539
  parser.evaluate_string(scope, source, __FILE__).should == result
@@ -920,8 +928,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
920
928
  param 'Integer', 'count'
921
929
  required_block_param
922
930
  end
923
- def test(count, block)
924
- block.call({}, *[].fill(10, 0, count))
931
+ def test(count)
932
+ yield(*[].fill(10, 0, count))
925
933
  end
926
934
  end
927
935
  the_func = fc.new({}, env_loader)
@@ -937,8 +945,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
937
945
  param 'Any', 'lambda_arg'
938
946
  required_block_param
939
947
  end
940
- def test(lambda_arg, block)
941
- block.call({}, lambda_arg)
948
+ def test(lambda_arg)
949
+ yield(lambda_arg)
942
950
  end
943
951
  end
944
952
  the_func = fc.new({}, env_loader)
@@ -1050,6 +1058,26 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
1050
1058
  expect(parser.evaluate_string(scope, source, __FILE__)).to eql('10')
1051
1059
  end
1052
1060
 
1061
+ it 'should accept a numeric variable expressed as $n' do
1062
+ source = '$x = "abc123def" =~ /(abc)(123)(def)/; "${$2}"'
1063
+ expect(parser.evaluate_string(scope, source, __FILE__)).to eql('123')
1064
+ end
1065
+
1066
+ it 'should accept a numeric variable expressed as just an integer' do
1067
+ source = '$x = "abc123def" =~ /(abc)(123)(def)/; "${2}"'
1068
+ expect(parser.evaluate_string(scope, source, __FILE__)).to eql('123')
1069
+ end
1070
+
1071
+ it 'should accept a numeric variable expressed as $n in an access operation' do
1072
+ source = '$x = "abc123def" =~ /(abc)(123)(def)/; "${$0[4,3]}"'
1073
+ expect(parser.evaluate_string(scope, source, __FILE__)).to eql('23d')
1074
+ end
1075
+
1076
+ it 'should accept a numeric variable expressed as just an integer in an access operation' do
1077
+ source = '$x = "abc123def" =~ /(abc)(123)(def)/; "${0[4,3]}"'
1078
+ expect(parser.evaluate_string(scope, source, __FILE__)).to eql('23d')
1079
+ end
1080
+
1053
1081
  {
1054
1082
  '"value is ${a*2} yo"' => :error,
1055
1083
  }.each do |source, result|
@@ -27,6 +27,7 @@ describe 'loaders' do
27
27
  include PuppetSpec::Files
28
28
 
29
29
  let(:module_without_metadata) { File.join(config_dir('wo_metadata_module'), 'modules') }
30
+ let(:mix_4x_and_3x_functions) { config_dir('mix_4x_and_3x_functions') }
30
31
  let(:module_with_metadata) { File.join(config_dir('single_module'), 'modules') }
31
32
  let(:dependent_modules_with_metadata) { config_dir('dependent_modules_with_metadata') }
32
33
  let(:empty_test_env) { environment_for() }
@@ -99,6 +100,26 @@ describe 'loaders' do
99
100
  expect(function.call({})).to eql("usee::callee() was told 'passed value' + I am user::caller()")
100
101
  end
101
102
 
103
+ context 'with scope' do
104
+ let(:env) { environment_for(mix_4x_and_3x_functions) }
105
+ let(:scope) { Puppet::Parser::Compiler.new(Puppet::Node.new("test", :environment => env)).newscope(nil) }
106
+ let(:loader) { Puppet::Pops::Loaders.new(env).private_loader_for_module('user') }
107
+
108
+ it 'can call 3x function in dependent module from a 4x function' do
109
+ Puppet.override({ :current_environment => scope.environment, :global_scope => scope }) do
110
+ function = loader.load_typed(typed_name(:function, 'user::caller')).value
111
+ expect(function.call(scope)).to eql("usee::callee() got 'first' - usee::callee() got 'second'")
112
+ end
113
+ end
114
+
115
+ it 'can call 3x function and propagate caller scope from a 4x function' do
116
+ Puppet.override({ :current_environment => scope.environment, :global_scope => scope }) do
117
+ function = loader.load_typed(typed_name(:function, 'user::caller_ws')).value
118
+ expect(function.call(scope, 'passed in scope')).to eql("usee::callee_ws() got 'passed in scope'")
119
+ end
120
+ end
121
+ end
122
+
102
123
  it 'can load a function more than once from modules' do
103
124
  env = environment_for(dependent_modules_with_metadata)
104
125
  loaders = Puppet::Pops::Loaders.new(env)
@@ -112,7 +133,7 @@ describe 'loaders' do
112
133
  end
113
134
 
114
135
  def environment_for(*module_paths)
115
- Puppet::Node::Environment.create(:'*test*', module_paths, '')
136
+ Puppet::Node::Environment.create(:'*test*', module_paths)
116
137
  end
117
138
 
118
139
  def typed_name(type, name)
@@ -118,10 +118,8 @@ describe 'Lexer2' do
118
118
  end
119
119
 
120
120
  [ '_x::y', 'x::_y'].each do |string|
121
- it "should consider the bare word '#{string}' to be a bad NAME" do
122
- expect {
123
- tokens_scanned_from(string)
124
- }.to raise_error(/Illegal fully qualified name/)
121
+ it "should consider the bare word '#{string}' to be a WORD" do
122
+ tokens_scanned_from(string).should match_tokens2(:WORD)
125
123
  end
126
124
  end
127
125
 
@@ -209,21 +207,33 @@ describe 'Lexer2' do
209
207
  end
210
208
  end
211
209
 
212
- { '"a$x b"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }],
213
- [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }],
214
- [:DQPOST, ' b', {:line => 1, :pos=>5, :length=>3 }]],
210
+ { '"a$x b"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }],
211
+ [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }],
212
+ [:DQPOST, ' b', {:line => 1, :pos=>5, :length=>3 }]],
215
213
 
216
- '"a$x.b"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }],
217
- [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }],
218
- [:DQPOST, '.b', {:line => 1, :pos=>5, :length=>3 }]],
214
+ '"a$x.b"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }],
215
+ [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }],
216
+ [:DQPOST, '.b', {:line => 1, :pos=>5, :length=>3 }]],
219
217
 
220
- '"$x.b"' => [[:DQPRE, '', {:line => 1, :pos=>1, :length=>1 }],
221
- [:VARIABLE, 'x', {:line => 1, :pos=>2, :length=>2 }],
222
- [:DQPOST, '.b', {:line => 1, :pos=>4, :length=>3 }]],
218
+ '"$x.b"' => [[:DQPRE, '', {:line => 1, :pos=>1, :length=>1 }],
219
+ [:VARIABLE, 'x', {:line => 1, :pos=>2, :length=>2 }],
220
+ [:DQPOST, '.b', {:line => 1, :pos=>4, :length=>3 }]],
223
221
 
224
- '"a$x"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }],
225
- [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }],
226
- [:DQPOST, '', {:line => 1, :pos=>5, :length=>1 }]],
222
+ '"a$x"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>2 }],
223
+ [:VARIABLE, 'x', {:line => 1, :pos=>3, :length=>2 }],
224
+ [:DQPOST, '', {:line => 1, :pos=>5, :length=>1 }]],
225
+
226
+ '"a${x}"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>4 }],
227
+ [:VARIABLE, 'x', {:line => 1, :pos=>5, :length=>1 }],
228
+ [:DQPOST, '', {:line => 1, :pos=>7, :length=>1 }]],
229
+
230
+ '"a${_x}"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>4 }],
231
+ [:VARIABLE, '_x', {:line => 1, :pos=>5, :length=>2 }],
232
+ [:DQPOST, '', {:line => 1, :pos=>8, :length=>1 }]],
233
+
234
+ '"a${y::_x}"' => [[:DQPRE, 'a', {:line => 1, :pos=>1, :length=>4 }],
235
+ [:VARIABLE, 'y::_x', {:line => 1, :pos=>5, :length=>5 }],
236
+ [:DQPOST, '', {:line => 1, :pos=>11, :length=>1 }]],
227
237
  }.each do |source, expected|
228
238
  it "should lex an interpolated variable 'x' from #{source}" do
229
239
  tokens_scanned_from(source).should match_tokens2(*expected)
@@ -301,6 +311,8 @@ describe 'Lexer2' do
301
311
  "]" => ["] /./", [:RBRACK, :DIV, :DOT, :DIV]],
302
312
  "|>" => ["|> /./", [:RCOLLECT, :DIV, :DOT, :DIV]],
303
313
  "|>>" => ["|>> /./", [:RRCOLLECT, :DIV, :DOT, :DIV]],
314
+ "$x" => ["$x /1/", [:VARIABLE, :DIV, :NUMBER, :DIV]],
315
+ "a-b" => ["a-b /1/", [:WORD, :DIV, :NUMBER, :DIV]],
304
316
  '"a$a"' => ['"a$a" /./', [:DQPRE, :VARIABLE, :DQPOST, :DIV, :DOT, :DIV]],
305
317
  }.each do |token, entry|
306
318
  it "should not lex regexp after '#{token}'" do
@@ -114,4 +114,25 @@ describe "egrammar parsing heredoc" do
114
114
  ].join("\n")
115
115
  end
116
116
 
117
+ it 'parses multiple heredocs on the same line' do
118
+ src = <<-CODE
119
+ notice({ @(foo) => @(bar) })
120
+ hello
121
+ -foo
122
+ world
123
+ -bar
124
+ notice '!'
125
+ CODE
126
+ expect(dump(parse(src))).to eq([
127
+ '(block',
128
+ ' (invoke notice ({} ((@()',
129
+ ' (sublocated \' hello\')',
130
+ ' ) (@()',
131
+ ' (sublocated \' world\')',
132
+ ' ))))',
133
+ ' (invoke notice \'!\')',
134
+ ')'
135
+ ].join("\n"))
136
+ end
137
+
117
138
  end
@@ -85,6 +85,14 @@ describe 'The type calculator' do
85
85
  Puppet::Pops::Types::TypeFactory.any()
86
86
  end
87
87
 
88
+ def optional_t(t)
89
+ Puppet::Pops::Types::TypeFactory.optional(t)
90
+ end
91
+
92
+ def undef_t
93
+ Puppet::Pops::Types::TypeFactory.undef
94
+ end
95
+
88
96
  def unit_t
89
97
  # Cannot be created via factory, the type is private to the type system
90
98
  Puppet::Pops::Types::PUnitType.new
@@ -99,7 +107,7 @@ describe 'The type calculator' do
99
107
  # Do not include the special type Unit in this list
100
108
  def all_types
101
109
  [ Puppet::Pops::Types::PAnyType,
102
- Puppet::Pops::Types::PNilType,
110
+ Puppet::Pops::Types::PUndefType,
103
111
  Puppet::Pops::Types::PDataType,
104
112
  Puppet::Pops::Types::PScalarType,
105
113
  Puppet::Pops::Types::PStringType,
@@ -175,7 +183,7 @@ describe 'The type calculator' do
175
183
  result << Puppet::Pops::Types::PDataType
176
184
  result << array_t(types::PDataType.new)
177
185
  result << types::TypeFactory.hash_of_data
178
- result << Puppet::Pops::Types::PNilType
186
+ result << Puppet::Pops::Types::PUndefType
179
187
  tmp = tuple_t(types::PDataType.new)
180
188
  result << (tmp)
181
189
  tmp.size_type = range_t(0, nil)
@@ -223,8 +231,8 @@ describe 'The type calculator' do
223
231
  calculator.infer(/^a regular expression$/).class.should == Puppet::Pops::Types::PRegexpType
224
232
  end
225
233
 
226
- it 'nil translates to PNilType' do
227
- calculator.infer(nil).class.should == Puppet::Pops::Types::PNilType
234
+ it 'nil translates to PUndefType' do
235
+ calculator.infer(nil).class.should == Puppet::Pops::Types::PUndefType
228
236
  end
229
237
 
230
238
  it ':undef translates to PRuntimeType' do
@@ -355,8 +363,48 @@ describe 'The type calculator' do
355
363
  it 'with fixnum values translates to PHashType[key, PIntegerType]' do
356
364
  calculator.infer({:first => 1, :second => 2}).element_type.class.should == Puppet::Pops::Types::PIntegerType
357
365
  end
358
- end
359
366
 
367
+ it 'when empty infers a type that answers true to is_the_empty_hash?' do
368
+ expect(calculator.infer({}).is_the_empty_hash?).to be_true
369
+ expect(calculator.infer_set({}).is_the_empty_hash?).to be_true
370
+ end
371
+
372
+ it 'when empty is assignable to any PHashType' do
373
+ expect(calculator.assignable?(hash_t(string_t, string_t), calculator.infer({}))).to be_true
374
+ end
375
+
376
+ it 'when empty is not assignable to a PHashType with from size > 0' do
377
+ expect(calculator.assignable?(constrained_t(hash_t(string_t,string_t), 1, 1), calculator.infer({}))).to be_false
378
+ end
379
+
380
+ context 'using infer_set' do
381
+ it "with 'first' and 'second' keys translates to PStructType[{first=>value,second=>value}]" do
382
+ t = calculator.infer_set({'first' => 1, 'second' => 2})
383
+ expect(t.class).to eq(Puppet::Pops::Types::PStructType)
384
+ expect(t.elements.size).to eq(2)
385
+ expect(t.elements.map { |e| e.name }.sort).to eq(['first', 'second'])
386
+ end
387
+
388
+ it 'with string keys and string and array values translates to PStructType[{key1=>PStringType,key2=>PTupleType}]' do
389
+ t = calculator.infer_set({ 'mode' => 'read', 'path' => ['foo', 'fee' ] })
390
+ expect(t.class).to eq(Puppet::Pops::Types::PStructType)
391
+ expect(t.elements.size).to eq(2)
392
+ els = t.elements.map { |e| e.type }.sort {|a,b| a.to_s <=> b.to_s }
393
+ els[0].class.should == Puppet::Pops::Types::PStringType
394
+ els[1].class.should == Puppet::Pops::Types::PTupleType
395
+ end
396
+
397
+ it 'with mixed string and non-string keys translates to PHashType' do
398
+ t = calculator.infer_set({ 1 => 'first', 'second' => 'second' })
399
+ expect(t.class).to eq(Puppet::Pops::Types::PHashType)
400
+ end
401
+
402
+ it 'with empty string keys translates to PHashType' do
403
+ t = calculator.infer_set({ '' => 'first', 'second' => 'second' })
404
+ expect(t.class).to eq(Puppet::Pops::Types::PHashType)
405
+ end
406
+ end
407
+ end
360
408
  end
361
409
 
362
410
  context 'patterns' do
@@ -683,6 +731,22 @@ describe 'The type calculator' do
683
731
  t = Puppet::Pops::Types::PHashType.new()
684
732
  tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
685
733
  end
734
+
735
+ it 'Struct is assignable to Hash with Pattern that matches all keys' do
736
+ struct_t({'x' => integer_t, 'y' => integer_t}).should be_assignable_to(hash_t(pattern_t(/^\w+$/), factory.any))
737
+ end
738
+
739
+ it 'Struct is assignable to Hash with Enum that matches all keys' do
740
+ struct_t({'x' => integer_t, 'y' => integer_t}).should be_assignable_to(hash_t(enum_t('x', 'y', 'z'), factory.any))
741
+ end
742
+
743
+ it 'Struct is not assignable to Hash with Pattern unless all keys match' do
744
+ struct_t({'a' => integer_t, 'A' => integer_t}).should_not be_assignable_to(hash_t(pattern_t(/^[A-Z]+$/), factory.any))
745
+ end
746
+
747
+ it 'Struct is not assignable to Hash with Enum unless all keys match' do
748
+ struct_t({'a' => integer_t, 'y' => integer_t}).should_not be_assignable_to(hash_t(enum_t('x', 'y', 'z'), factory.any))
749
+ end
686
750
  end
687
751
 
688
752
  context "for Tuple, such that" do
@@ -739,7 +803,7 @@ describe 'The type calculator' do
739
803
  Bignum => Puppet::Pops::Types::PIntegerType.new,
740
804
  Float => Puppet::Pops::Types::PFloatType.new,
741
805
  Numeric => Puppet::Pops::Types::PNumericType.new,
742
- NilClass => Puppet::Pops::Types::PNilType.new,
806
+ NilClass => Puppet::Pops::Types::PUndefType.new,
743
807
  TrueClass => Puppet::Pops::Types::PBooleanType.new,
744
808
  FalseClass => Puppet::Pops::Types::PBooleanType.new,
745
809
  String => Puppet::Pops::Types::PStringType.new,
@@ -1025,6 +1089,30 @@ describe 'The type calculator' do
1025
1089
  calculator.assignable?(struct2, struct1).should == true
1026
1090
  end
1027
1091
 
1092
+ it 'should accept matching structs with less elements when unmatched elements are optional' do
1093
+ struct1 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>optional_t(Integer)})
1094
+ struct2 = struct_t({'a'=>Integer, 'b'=>Integer})
1095
+ calculator.assignable?(struct1, struct2).should == true
1096
+ end
1097
+
1098
+ it 'should reject matching structs with more elements even if excess elements are optional' do
1099
+ struct1 = struct_t({'a'=>Integer, 'b'=>Integer})
1100
+ struct2 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>optional_t(Integer)})
1101
+ calculator.assignable?(struct1, struct2).should == false
1102
+ end
1103
+
1104
+ it 'should accept matching structs where one is more general than the other with respect to optional' do
1105
+ struct1 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>optional_t(Integer)})
1106
+ struct2 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>Integer})
1107
+ calculator.assignable?(struct1, struct2).should == true
1108
+ end
1109
+
1110
+ it 'should reject matching structs where one is more special than the other with respect to optional' do
1111
+ struct1 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>Integer})
1112
+ struct2 = struct_t({'a'=>Integer, 'b'=>Integer, 'c'=>optional_t(Integer)})
1113
+ calculator.assignable?(struct1, struct2).should == false
1114
+ end
1115
+
1028
1116
  it 'should accept matching structs where one is more general than the other' do
1029
1117
  struct1 = struct_t({'a'=>Integer, 'b'=>Integer})
1030
1118
  struct2 = struct_t({'a'=>Numeric, 'b'=>Numeric})
@@ -1041,6 +1129,13 @@ describe 'The type calculator' do
1041
1129
  calculator.assignable?(struct1, hsh).should == true
1042
1130
  calculator.assignable?(hsh, struct1).should == true
1043
1131
  end
1132
+
1133
+ it 'should accept empty hash with key_type undef' do
1134
+ struct1 = struct_t({'a'=>optional_t(Integer)})
1135
+ hsh = hash_t(undef_t, undef_t)
1136
+ factory.constrain_size(hsh, 0, 0)
1137
+ calculator.assignable?(struct1, hsh).should == true
1138
+ end
1044
1139
  end
1045
1140
 
1046
1141
  it 'should recognize ruby type inheritance' do
@@ -1117,7 +1212,7 @@ describe 'The type calculator' do
1117
1212
  include_context "types_setup"
1118
1213
 
1119
1214
  it 'should consider undef to be instance of Any, NilType, and optional' do
1120
- calculator.instance?(Puppet::Pops::Types::PNilType.new(), nil).should == true
1215
+ calculator.instance?(Puppet::Pops::Types::PUndefType.new(), nil).should == true
1121
1216
  calculator.instance?(Puppet::Pops::Types::PAnyType.new(), nil).should == true
1122
1217
  calculator.instance?(Puppet::Pops::Types::POptionalType.new(), nil).should == true
1123
1218
  end
@@ -1139,7 +1234,7 @@ describe 'The type calculator' do
1139
1234
  it 'should not consider undef to be an instance of any other type than Any, NilType and Data' do
1140
1235
  types_to_test = all_types - [
1141
1236
  Puppet::Pops::Types::PAnyType,
1142
- Puppet::Pops::Types::PNilType,
1237
+ Puppet::Pops::Types::PUndefType,
1143
1238
  Puppet::Pops::Types::PDataType,
1144
1239
  Puppet::Pops::Types::POptionalType,
1145
1240
  ]
@@ -1252,12 +1347,39 @@ describe 'The type calculator' do
1252
1347
  calculator.instance?(tuple, [1, 'a', 1]).should == false
1253
1348
  end
1254
1349
 
1255
- it 'should consider hash[cont] as instance of Struct[cont-t]' do
1256
- struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>Float})
1257
- calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>3.14}).should == true
1258
- calculator.instance?(struct, {'a'=>1.2, 'b'=>'a', 'c'=>3.14}).should == false
1259
- calculator.instance?(struct, {'a'=>1, 'b'=>1, 'c'=>3.14}).should == false
1260
- calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>1}).should == false
1350
+ context 'and t is Struct' do
1351
+ it 'should consider hash[cont] as instance of Struct[cont-t]' do
1352
+ struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>Float})
1353
+ calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>3.14}).should == true
1354
+ calculator.instance?(struct, {'a'=>1.2, 'b'=>'a', 'c'=>3.14}).should == false
1355
+ calculator.instance?(struct, {'a'=>1, 'b'=>1, 'c'=>3.14}).should == false
1356
+ calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>1}).should == false
1357
+ end
1358
+
1359
+ it 'should consider empty hash as instance of Struct[x=>Optional[String]]' do
1360
+ struct = struct_t({'a'=>optional_t(String)})
1361
+ calculator.instance?(struct, {}).should == true
1362
+ end
1363
+
1364
+ it 'should consider hash[cont] as instance of Struct[cont-t,optionals]' do
1365
+ struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>optional_t(Float)})
1366
+ calculator.instance?(struct, {'a'=>1, 'b'=>'a'}).should == true
1367
+ end
1368
+
1369
+ it 'should consider hash[cont] as instance of Struct[cont-t,variants with optionals]' do
1370
+ struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>variant_t(String, optional_t(Float))})
1371
+ calculator.instance?(struct, {'a'=>1, 'b'=>'a'}).should == true
1372
+ end
1373
+
1374
+ it 'should not consider hash[cont,cont2] as instance of Struct[cont-t]' do
1375
+ struct = struct_t({'a'=>Integer, 'b'=>String})
1376
+ calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>'x'}).should == false
1377
+ end
1378
+
1379
+ it 'should not consider hash[cont,cont2] as instance of Struct[cont-t,optional[cont3-t]' do
1380
+ struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>optional_t(Float)})
1381
+ calculator.instance?(struct, {'a'=>1, 'b'=>'a', 'c'=>'x'}).should == false
1382
+ end
1261
1383
  end
1262
1384
 
1263
1385
  context 'and t is Data' do
@@ -1343,8 +1465,8 @@ describe 'The type calculator' do
1343
1465
  end
1344
1466
  end
1345
1467
 
1346
- it 'should yield \'PNilType\' for NilClass' do
1347
- calculator.type(NilClass).class.should == Puppet::Pops::Types::PNilType
1468
+ it 'should yield \'PUndefType\' for NilClass' do
1469
+ calculator.type(NilClass).class.should == Puppet::Pops::Types::PUndefType
1348
1470
  end
1349
1471
 
1350
1472
  it 'should yield \'PStringType\' for String' do
@@ -1607,7 +1729,7 @@ describe 'The type calculator' do
1607
1729
  context 'when processing meta type' do
1608
1730
  it 'should infer PType as the type of all other types' do
1609
1731
  ptype = Puppet::Pops::Types::PType
1610
- calculator.infer(Puppet::Pops::Types::PNilType.new() ).is_a?(ptype).should() == true
1732
+ calculator.infer(Puppet::Pops::Types::PUndefType.new() ).is_a?(ptype).should() == true
1611
1733
  calculator.infer(Puppet::Pops::Types::PDataType.new() ).is_a?(ptype).should() == true
1612
1734
  calculator.infer(Puppet::Pops::Types::PScalarType.new() ).is_a?(ptype).should() == true
1613
1735
  calculator.infer(Puppet::Pops::Types::PStringType.new() ).is_a?(ptype).should() == true
@@ -1632,7 +1754,7 @@ describe 'The type calculator' do
1632
1754
 
1633
1755
  it 'should infer PType as the type of all other types' do
1634
1756
  ptype = Puppet::Pops::Types::PType
1635
- calculator.string(calculator.infer(Puppet::Pops::Types::PNilType.new() )).should == "Type[Undef]"
1757
+ calculator.string(calculator.infer(Puppet::Pops::Types::PUndefType.new() )).should == "Type[Undef]"
1636
1758
  calculator.string(calculator.infer(Puppet::Pops::Types::PDataType.new() )).should == "Type[Data]"
1637
1759
  calculator.string(calculator.infer(Puppet::Pops::Types::PScalarType.new() )).should == "Type[Scalar]"
1638
1760
  calculator.string(calculator.infer(Puppet::Pops::Types::PStringType.new() )).should == "Type[String]"
@@ -1775,7 +1897,7 @@ describe 'The type calculator' do
1775
1897
  inferred_type.class.should == Puppet::Pops::Types::PTupleType
1776
1898
  element_types = inferred_type.types
1777
1899
  element_types[0].class.should == Puppet::Pops::Types::PStringType
1778
- element_types[1].class.should == Puppet::Pops::Types::PNilType
1900
+ element_types[1].class.should == Puppet::Pops::Types::PUndefType
1779
1901
  end
1780
1902
  end
1781
1903