puppet 5.1.0-x64-mingw32 → 5.2.0-x64-mingw32

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 (108) hide show
  1. data/lib/puppet.rb +6 -53
  2. data/lib/puppet/application.rb +14 -7
  3. data/lib/puppet/application/agent.rb +6 -2
  4. data/lib/puppet/application/apply.rb +6 -2
  5. data/lib/puppet/application/cert.rb +6 -2
  6. data/lib/puppet/application/describe.rb +6 -2
  7. data/lib/puppet/application/device.rb +40 -29
  8. data/lib/puppet/application/doc.rb +6 -2
  9. data/lib/puppet/application/filebucket.rb +6 -2
  10. data/lib/puppet/application/lookup.rb +6 -2
  11. data/lib/puppet/application/master.rb +6 -2
  12. data/lib/puppet/application/resource.rb +6 -2
  13. data/lib/puppet/face/catalog.rb +1 -1
  14. data/lib/puppet/face/certificate_request.rb +1 -1
  15. data/lib/puppet/face/certificate_revocation_list.rb +1 -1
  16. data/lib/puppet/face/help.rb +17 -13
  17. data/lib/puppet/file_serving/configuration.rb +3 -0
  18. data/lib/puppet/file_serving/configuration/parser.rb +2 -0
  19. data/lib/puppet/file_serving/mount/tasks.rb +21 -0
  20. data/lib/puppet/functions/epp.rb +3 -0
  21. data/lib/puppet/functions/lookup.rb +2 -1
  22. data/lib/puppet/generate/models/type/property.rb +1 -1
  23. data/lib/puppet/gettext/config.rb +70 -0
  24. data/lib/puppet/gettext/stubs.rb +11 -0
  25. data/lib/puppet/indirector/request.rb +4 -4
  26. data/lib/puppet/info_service.rb +10 -0
  27. data/lib/puppet/info_service/task_information_service.rb +32 -0
  28. data/lib/puppet/module.rb +43 -12
  29. data/lib/puppet/module/task.rb +90 -0
  30. data/lib/puppet/parser/ast/leaf.rb +1 -1
  31. data/lib/puppet/parser/functions/dig.rb +11 -2
  32. data/lib/puppet/parser/functions/epp.rb +3 -0
  33. data/lib/puppet/parser/functions/new.rb +8 -8
  34. data/lib/puppet/pops/evaluator/evaluator_impl.rb +1 -1
  35. data/lib/puppet/pops/evaluator/runtime3_support.rb +1 -0
  36. data/lib/puppet/pops/issues.rb +11 -5
  37. data/lib/puppet/pops/loader/static_loader.rb +1 -1
  38. data/lib/puppet/pops/model/factory.rb +42 -2
  39. data/lib/puppet/pops/model/model_tree_dumper.rb +1 -1
  40. data/lib/puppet/pops/parser/egrammar.ra +30 -9
  41. data/lib/puppet/pops/parser/eparser.rb +1094 -1043
  42. data/lib/puppet/pops/patterns.rb +1 -1
  43. data/lib/puppet/pops/serialization/from_data_converter.rb +1 -1
  44. data/lib/puppet/pops/serialization/json_path.rb +1 -1
  45. data/lib/puppet/pops/serialization/to_data_converter.rb +12 -3
  46. data/lib/puppet/pops/types/p_object_type.rb +31 -3
  47. data/lib/puppet/pops/types/p_sem_ver_range_type.rb +3 -3
  48. data/lib/puppet/pops/types/p_timespan_type.rb +1 -1
  49. data/lib/puppet/pops/types/p_timestamp_type.rb +1 -1
  50. data/lib/puppet/pops/types/string_converter.rb +15 -12
  51. data/lib/puppet/pops/types/type_calculator.rb +1 -1
  52. data/lib/puppet/pops/types/type_factory.rb +7 -0
  53. data/lib/puppet/pops/types/type_formatter.rb +1 -1
  54. data/lib/puppet/pops/types/type_mismatch_describer.rb +86 -130
  55. data/lib/puppet/pops/types/type_parser.rb +10 -4
  56. data/lib/puppet/pops/types/types.rb +81 -22
  57. data/lib/puppet/provider/package/aix.rb +4 -4
  58. data/lib/puppet/provider/package/yum.rb +1 -0
  59. data/lib/puppet/type.rb +1 -1
  60. data/lib/puppet/type/mount.rb +1 -1
  61. data/lib/puppet/type/sshkey.rb +9 -1
  62. data/lib/puppet/util.rb +1 -1
  63. data/lib/puppet/util/command_line.rb +1 -1
  64. data/lib/puppet/util/log.rb +15 -10
  65. data/lib/puppet/util/windows/api_types.rb +9 -5
  66. data/lib/puppet/util/windows/process.rb +9 -1
  67. data/lib/puppet/vendor/semantic_puppet/lib/semantic_puppet.rb +1 -3
  68. data/lib/puppet/version.rb +1 -1
  69. data/locales/ja/puppet.po +9270 -0
  70. data/locales/puppet.pot +272 -212
  71. data/spec/fixtures/unit/provider/package/yum/yum-check-update-plugin-output.txt +36 -0
  72. data/spec/integration/indirector/file_content/file_server_spec.rb +17 -0
  73. data/spec/integration/indirector/file_metadata/file_server_spec.rb +10 -0
  74. data/spec/integration/util/windows/process_spec.rb +45 -0
  75. data/spec/lib/puppet_spec/modules.rb +10 -0
  76. data/spec/shared_contexts/types_setup.rb +19 -4
  77. data/spec/spec_helper.rb +6 -7
  78. data/spec/unit/face/help_spec.rb +2 -2
  79. data/spec/unit/file_serving/configuration_spec.rb +14 -4
  80. data/spec/unit/file_serving/mount/modules_spec.rb +1 -1
  81. data/spec/unit/file_serving/mount/tasks_spec.rb +72 -0
  82. data/spec/unit/functions/epp_spec.rb +5 -0
  83. data/spec/unit/functions/regsubst_spec.rb +1 -1
  84. data/spec/unit/gettext_config_spec.rb +57 -0
  85. data/spec/unit/indirector/request_spec.rb +41 -0
  86. data/spec/unit/info_service_spec.rb +66 -2
  87. data/spec/unit/module_spec.rb +81 -1
  88. data/spec/unit/parser/ast/leaf_spec.rb +3 -4
  89. data/spec/unit/pops/evaluator/access_ops_spec.rb +5 -0
  90. data/spec/unit/pops/factory_spec.rb +5 -1
  91. data/spec/unit/pops/parser/parser_spec.rb +138 -0
  92. data/spec/unit/pops/serialization/to_from_hr_spec.rb +74 -1
  93. data/spec/unit/pops/types/p_init_type_spec.rb +1 -1
  94. data/spec/unit/pops/types/p_object_type_spec.rb +217 -33
  95. data/spec/unit/pops/types/p_timespan_type_spec.rb +7 -0
  96. data/spec/unit/pops/types/p_timestamp_type_spec.rb +7 -0
  97. data/spec/unit/pops/types/string_converter_spec.rb +48 -11
  98. data/spec/unit/pops/types/type_calculator_spec.rb +37 -5
  99. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +12 -0
  100. data/spec/unit/pops/types/type_parser_spec.rb +25 -0
  101. data/spec/unit/pops/validator/validator_spec.rb +2 -2
  102. data/spec/unit/provider/package/aix_spec.rb +26 -1
  103. data/spec/unit/provider/package/yum_spec.rb +10 -0
  104. data/spec/unit/task_spec.rb +102 -0
  105. data/spec/unit/util/log_spec.rb +32 -3
  106. data/spec/unit/util/windows/api_types_spec.rb +51 -0
  107. metadata +3488 -3452
  108. checksums.yaml +0 -7
@@ -862,22 +862,53 @@ describe 'The type calculator' do
862
862
  context 'for Data, such that' do
863
863
  let(:data) { TypeFactory.data }
864
864
  data_compatible_types.map { |t2| type_from_class(t2) }.each do |tc|
865
- it "#{tc.name} is assignable to Data" do
865
+ it "it is assignable from #{tc.name}" do
866
866
  expect(tc).to be_assignable_to(data)
867
867
  end
868
868
  end
869
869
 
870
- it 'Data is not assignable to any of its subtypes' do
870
+ data_compatible_types.map { |t2| type_from_class(t2) }.each do |tc|
871
+ it "it is assignable from Optional[#{tc.name}]" do
872
+ expect(optional_t(tc)).to be_assignable_to(data)
873
+ end
874
+ end
875
+
876
+ it 'it is not assignable to any of its subtypes' do
871
877
  types_to_test = data_compatible_types
872
878
  types_to_test.each {|t2| expect(data).not_to be_assignable_to(type_from_class(t2)) }
873
879
  end
874
880
 
875
- it 'Data is not assignable to any disjunct type' do
876
- tested_types = all_types - [PAnyType, POptionalType, PInitType] - scalar_types
881
+ it 'it is not assignable to any disjunct type' do
882
+ tested_types = all_types - [PAnyType, POptionalType, PInitType] - scalar_data_types
877
883
  tested_types.each {|t2| expect(data).not_to be_assignable_to(t2::DEFAULT) }
878
884
  end
879
885
  end
880
886
 
887
+ context 'for Rich Data, such that' do
888
+ let(:rich_data) { TypeFactory.rich_data }
889
+ rich_data_compatible_types.map { |t2| type_from_class(t2) }.each do |tc|
890
+ it "it is assignable from #{tc.name}" do
891
+ expect(tc).to be_assignable_to(rich_data)
892
+ end
893
+ end
894
+
895
+ rich_data_compatible_types.map { |t2| type_from_class(t2) }.each do |tc|
896
+ it "it is assignable from Optional[#{tc.name}]" do
897
+ expect(optional_t(tc)).to be_assignable_to(rich_data)
898
+ end
899
+ end
900
+
901
+ it 'it is not assignable to any of its subtypes' do
902
+ types_to_test = rich_data_compatible_types
903
+ types_to_test.each {|t2| expect(rich_data).not_to be_assignable_to(type_from_class(t2)) }
904
+ end
905
+
906
+ it 'it is not assignable to any disjunct type' do
907
+ tested_types = all_types - [PAnyType, POptionalType, PInitType] - scalar_types
908
+ tested_types.each {|t2| expect(rich_data).not_to be_assignable_to(t2::DEFAULT) }
909
+ end
910
+ end
911
+
881
912
  context 'for Variant, such that' do
882
913
  it 'it is assignable to a type if all contained types are assignable to that type' do
883
914
  v = variant_t(range_t(10, 12),range_t(14, 20))
@@ -1744,11 +1775,12 @@ describe 'The type calculator' do
1744
1775
  expect(calculator.instance?(PAnyType::DEFAULT, :default)).to eq(true)
1745
1776
  end
1746
1777
 
1747
- it 'should not consider "default" to be an instance of anything but Default, NotUndef, and Any' do
1778
+ it 'should not consider "default" to be an instance of anything but Default, Init, NotUndef, and Any' do
1748
1779
  types_to_test = all_types - [
1749
1780
  PAnyType,
1750
1781
  PNotUndefType,
1751
1782
  PDefaultType,
1783
+ PInitType,
1752
1784
  ]
1753
1785
 
1754
1786
  types_to_test.each {|t| expect(calculator.instance?(t::DEFAULT, :default)).to eq(false) }
@@ -223,6 +223,18 @@ describe 'the type mismatch describer' do
223
223
  expect(subject.describe_signatures('function', [dispatch], args_tuple)).to eq("'function' block return expects a String value, got Integer")
224
224
  end
225
225
  end
226
+
227
+ it "reports struct mismatch correctly when hash doesn't contain required keys" do
228
+ code = <<-PUPPET
229
+ type Test::Options = Struct[{
230
+ var => String
231
+ }]
232
+ class test(String $var, Test::Options $opts) {}
233
+ class { 'test': var => 'hello', opts => {} }
234
+ PUPPET
235
+ expect { eval_and_collect_notices(code) }.to(raise_error(Puppet::Error,
236
+ /Class\[Test\]: parameter 'opts' expects size to be 1, got 0/))
237
+ end
226
238
  end
227
239
  end
228
240
  end
@@ -30,6 +30,11 @@ describe TypeParser do
30
30
  /The expression <Array\[notAType\]> is not a valid type specification/)
31
31
  end
32
32
 
33
+ it "rejects an unknown type parameter in a variant" do
34
+ expect { parser.parse("Variant[Integer,'not a type']") }.to raise_error(Puppet::ParseError,
35
+ /The expression <Variant\[Integer,'not a type'\]> is not a valid type specification/)
36
+ end
37
+
33
38
  [
34
39
  'Any', 'Data', 'CatalogEntry', 'Boolean', 'Scalar', 'Undef', 'Numeric', 'Default'
35
40
  ].each do |name|
@@ -371,6 +376,26 @@ describe TypeParser do
371
376
  expect(t.type_string).to eql('Nonesuch[{a=>undef,b=>true,c=>false,d=>default,e=>"string",f=>0,g=>1.0,h=>[1,2,3]}]')
372
377
  end
373
378
 
379
+ it 'parses a parameterized Enum using identifiers' do
380
+ t = parser.parse('Enum[a, b]')
381
+ expect(t).to be_a(PEnumType)
382
+ expect(t.to_s).to eql("Enum['a', 'b']")
383
+ end
384
+
385
+ it 'parses a parameterized Enum using strings' do
386
+ t = parser.parse("Enum['a', 'b']")
387
+ expect(t).to be_a(PEnumType)
388
+ expect(t.to_s).to eql("Enum['a', 'b']")
389
+ end
390
+
391
+ it 'rejects a parameterized Enum using type refs' do
392
+ expect { parser.parse('Enum[A, B]') }.to raise_error(/Enum parameters must be identifiers or strings/)
393
+ end
394
+
395
+ it 'rejects a parameterized Enum using integers' do
396
+ expect { parser.parse('Enum[1, 2]') }.to raise_error(/Enum parameters must be identifiers or strings/)
397
+ end
398
+
374
399
  matcher :be_the_type do |type|
375
400
  calc = TypeCalculator.new
376
401
 
@@ -472,12 +472,12 @@ describe "validating 4x" do
472
472
  context 'that are type aliases' do
473
473
  it 'raises errors when RHS is a name that is an invalid reference' do
474
474
  source = 'type MyInt = integer'
475
- expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_EXPRESSION)
475
+ expect { parse(source) }.to raise_error(/Syntax error at 'integer'/)
476
476
  end
477
477
 
478
478
  it 'raises errors when RHS is an AccessExpression with a name that is an invalid reference on LHS' do
479
479
  source = 'type IntegerArray = array[Integer]'
480
- expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_EXPRESSION)
480
+ expect { parse(source) }.to raise_error(/Syntax error at 'array'/)
481
481
  end
482
482
  end
483
483
 
@@ -25,7 +25,6 @@ describe provider_class do
25
25
 
26
26
  describe "when installing" do
27
27
  it "should install a package" do
28
- @resource.stubs(:should).with(:ensure).returns(:installed)
29
28
  @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage')
30
29
  @provider.install
31
30
  end
@@ -98,10 +97,36 @@ mypackage 1.2.3.3 Already superseded by 1.2.3.4
98
97
  @provider.stubs(:latest_info).returns( { :version => "1.2.3.5" } )
99
98
  expect(@provider.latest).to eq("1.2.3.5")
100
99
  end
100
+
101
+ it "should prefetch the right values" do
102
+ Process.stubs(:euid).returns(0)
103
+ resource = Puppet::Type.type(:package).
104
+ new(:name => 'sudo.rte', :ensure => :latest,
105
+ :source => 'mysource', :provider => :aix)
106
+
107
+ resource.stubs(:should).with(:ensure).returns(:latest)
108
+ resource.should(:ensure)
109
+
110
+ resource.provider.class.stubs(:execute).returns(<<-END.chomp)
111
+ sudo:sudo.rte:1.7.10.4::I:C:::::N:Configurable super-user privileges runtime::::0::
112
+ sudo:sudo.rte:1.8.6.4::I:T:::::N:Configurable super-user privileges runtime::::0::
113
+ END
114
+
115
+ resource.provider.class.prefetch('sudo.rte' => resource)
116
+ expect(resource.provider.latest).to eq('1.8.6.4')
117
+ end
101
118
  end
102
119
 
103
120
  it "update should install a package" do
104
121
  @provider.expects(:install).with(false)
105
122
  @provider.update
106
123
  end
124
+
125
+ it "should prefetch when some packages lack sources" do
126
+ latest = Puppet::Type.type(:package).new(:name => 'mypackage', :ensure => :latest, :source => 'mysource', :provider => :aix)
127
+ absent = Puppet::Type.type(:package).new(:name => 'otherpackage', :ensure => :absent, :provider => :aix)
128
+ Process.stubs(:euid).returns(0)
129
+ provider_class.expects(:execute).returns 'mypackage:mypackage.rte:1.8.6.4::I:T:::::N:A Super Cool Package::::0::\n'
130
+ provider_class.prefetch({ 'mypackage' => latest, 'otherpackage' => absent })
131
+ end
107
132
  end
@@ -69,5 +69,15 @@ describe provider_class do
69
69
  }.to raise_exception(Exception, /Failed to parse/)
70
70
  end
71
71
  end
72
+ describe "with trailing plugin output" do
73
+ let(:check_update) { File.read(my_fixture("yum-check-update-plugin-output.txt")) }
74
+ let(:output) { described_class.parse_updates(check_update) }
75
+ it "parses correctly formatted entries" do
76
+ expect(output['bash']).to eq([{:name => 'bash', :epoch => '0', :version => '4.2.46', :release => '12.el7', :arch => 'x86_64'}])
77
+ end
78
+ it "ignores all mentions of plugin output" do
79
+ expect(output).not_to include("Random plugin")
80
+ end
81
+ end
72
82
  end
73
83
  end
@@ -0,0 +1,102 @@
1
+ #! /usr/bin/env ruby
2
+ require 'spec_helper'
3
+ require 'puppet_spec/files'
4
+ require 'puppet_spec/modules'
5
+ require 'puppet/module/task'
6
+
7
+ describe Puppet::Module::Task do
8
+ include PuppetSpec::Files
9
+
10
+ let(:modpath) { tmpdir('task_modpath') }
11
+ let(:mymodpath) { File.join(modpath, 'mymod') }
12
+ let(:mymod) { Puppet::Module.new('mymod', mymodpath, nil) }
13
+ let(:tasks_path) { File.join(mymodpath, 'tasks') }
14
+ let(:tasks_glob) { File.join(mymodpath, 'tasks', '*') }
15
+
16
+ it "cannot construct tasks with illegal names" do
17
+ expect { Puppet::Module::Task.new(mymod, "iLegal", []) }
18
+ .to raise_error(Puppet::Module::Task::InvalidName,
19
+ "Task names must start with a lowercase letter and be composed of only lowercase letters, numbers, and underscores")
20
+ end
21
+
22
+ it "cannot construct tasks whose files are outside of their module's tasks directory" do
23
+ outside_file = "/var/root/secret_tasks/classified"
24
+ expect { Puppet::Module::Task.new(mymod, "fail", [outside_file]) }
25
+ .to raise_error(Puppet::Module::Task::InvalidFile,
26
+ "The file '#{outside_file}' is not located in the mymod module's tasks directory")
27
+ end
28
+
29
+ it "constructs tasks as expected when every task has a metadata file with the same name (besides extension)" do
30
+ Dir.expects(:glob).with(tasks_glob).returns(%w{task1.json task1 task2.json task2.exe task3.json task3.sh})
31
+ tasks = Puppet::Module::Task.tasks_in_module(mymod)
32
+
33
+ expect(tasks.count).to eq(3)
34
+ expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1 mymod::task2 mymod::task3})
35
+ expect(tasks.map{|t| t.metadata_file}).to eq(["#{tasks_path}/task1.json",
36
+ "#{tasks_path}/task2.json",
37
+ "#{tasks_path}/task3.json"])
38
+ expect(tasks.map{|t| t.files}).to eq([["#{tasks_path}/task1"],
39
+ ["#{tasks_path}/task2.exe"],
40
+ ["#{tasks_path}/task3.sh"]])
41
+
42
+ tasks.map{|t| t.metadata_file}.each do |metadata_file|
43
+ expect(metadata_file).to eq(File.absolute_path(metadata_file))
44
+ end
45
+
46
+ tasks.map{|t| t.files}.each do |task_files|
47
+ task_file = task_files[0]
48
+ expect(task_file).to eq(File.absolute_path(task_file))
49
+ end
50
+ end
51
+
52
+ it "constructs tasks as expected when some tasks don't have a metadata file" do
53
+ Dir.expects(:glob).with(tasks_glob).returns(%w{task1 task2.exe task3.json task3.sh})
54
+ tasks = Puppet::Module::Task.tasks_in_module(mymod)
55
+
56
+ expect(tasks.count).to eq(3)
57
+ expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1 mymod::task2 mymod::task3})
58
+ expect(tasks.map{|t| t.metadata_file}).to eq([nil, nil, "#{tasks_path}/task3.json"])
59
+ expect(tasks.map{|t| t.files}).to eq([["#{tasks_path}/task1"],
60
+ ["#{tasks_path}/task2.exe"],
61
+ ["#{tasks_path}/task3.sh"]])
62
+ end
63
+
64
+ it "constructs tasks as expected when a task has multiple executable files" do
65
+ Dir.expects(:glob).with(tasks_glob).returns(%w{task1.elf task1.exe task1.json task2.ps1 task2.sh})
66
+ tasks = Puppet::Module::Task.tasks_in_module(mymod)
67
+
68
+ expect(tasks.count).to eq(2)
69
+ expect(tasks.map{|t| t.name}).to eq(%w{mymod::task1 mymod::task2})
70
+ expect(tasks.map{|t| t.metadata_file}).to eq(["#{tasks_path}/task1.json", nil])
71
+ expect(tasks.map{|t| t.files}).to eq([["#{tasks_path}/task1.elf", "#{tasks_path}/task1.exe"],
72
+ ["#{tasks_path}/task2.ps1", "#{tasks_path}/task2.sh"]])
73
+ end
74
+
75
+ it "finds files whose names (besides extensions) are valid task names" do
76
+ Dir.expects(:glob).with(tasks_glob).returns(%w{task task_1 xx_t_a_s_k_2_xx})
77
+ tasks = Puppet::Module::Task.tasks_in_module(mymod)
78
+
79
+ expect(tasks.count).to eq(3)
80
+ expect(tasks.map{|t| t.name}).to eq(%w{mymod::task mymod::task_1 mymod::xx_t_a_s_k_2_xx})
81
+ end
82
+
83
+ it "ignores files that have names (besides extensions) that are not valid task names" do
84
+ Dir.expects(:glob).with(tasks_glob).returns(%w{.nottask.exe .wat !runme _task 2task2furious def_a_task_PSYCH Fake_task not-a-task realtask})
85
+ tasks = Puppet::Module::Task.tasks_in_module(mymod)
86
+
87
+ expect(tasks.count).to eq(1)
88
+ expect(tasks.map{|t| t.name}).to eq(%w{mymod::realtask})
89
+ end
90
+
91
+ it "ignores files that have names ending in .conf and .md" do
92
+ Dir.expects(:glob).with(tasks_glob).returns(%w{ginuwine_task task.conf readme.md other_task.md})
93
+ tasks = Puppet::Module::Task.tasks_in_module(mymod)
94
+
95
+ expect(tasks.count).to eq(1)
96
+ expect(tasks.map{|t| t.name}).to eq(%w{mymod::ginuwine_task})
97
+ end
98
+
99
+ it "gives the 'init' task a name that is just the module's name" do
100
+ expect(Puppet::Module::Task.new(mymod, 'init', ["#{tasks_path}/init.sh"]).name).to eq('mymod')
101
+ end
102
+ end
@@ -32,11 +32,11 @@ describe Puppet::Util::Log do
32
32
  end
33
33
 
34
34
  it "includes a backtrace in the log" do
35
- expect(logs.last.message).to match(/Backtrace:\n.*in `initialize'/ )
35
+ expect(logs.last.message).to match(/Backtrace:\n.*in `newmessage'\n.*in `initialize'/ )
36
36
  end
37
37
 
38
38
  it "warns that message included invalid encoding" do
39
- expect(logs.last.message).to match(/Received a log message with invalid encoding/)
39
+ expect(logs.last.message).to match(/Received a Log attribute with invalid encoding/)
40
40
  end
41
41
 
42
42
  it "includes the 'dump' of the invalid message" do
@@ -44,9 +44,18 @@ describe Puppet::Util::Log do
44
44
  end
45
45
  end
46
46
 
47
+ # need a string that cannot be converted to US-ASCII or other encodings easily
48
+ # different UTF-8 widths
49
+ # 1-byte A
50
+ # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191
51
+ # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160
52
+ # 4-byte ܎ - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142
53
+ let (:mixed_utf8) { "A\u06FF\u16A0\u{2070E}" } # Aۿᚠ܎
54
+
47
55
  it "converts a given non-UTF-8 message to UTF-8" do
48
56
  logs = []
49
57
  Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(logs))
58
+ Puppet::Util::Log.newdestination(:console)
50
59
 
51
60
  # HIRAGANA LETTER SO
52
61
  # In Windows_31J: \x82 \xbb - 130 187
@@ -54,10 +63,30 @@ describe Puppet::Util::Log do
54
63
  win_31j_msg = [130, 187].pack('C*').force_encoding(Encoding::Windows_31J)
55
64
  utf_8_msg = "\u305d"
56
65
 
57
- Puppet::Util::Log.new(:level => :notice, :message => win_31j_msg, :source => 'Puppet')
66
+ $stdout.expects(:puts).with("\e[mNotice: #{mixed_utf8}: #{utf_8_msg}\e[0m")
67
+
68
+ # most handlers do special things with a :source => 'Puppet', so use something else
69
+ Puppet::Util::Log.new(:level => :notice, :message => win_31j_msg, :source => mixed_utf8)
58
70
  expect(logs.last.message).to eq(utf_8_msg)
59
71
  end
60
72
 
73
+ it "converts a given non-UTF-8 source to UTF-8" do
74
+ logs = []
75
+ Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(logs))
76
+ Puppet::Util::Log.newdestination(:console)
77
+
78
+ # HIRAGANA LETTER SO
79
+ # In Windows_31J: \x82 \xbb - 130 187
80
+ # In Unicode: \u305d - \xe3 \x81 \x9d - 227 129 157
81
+ win_31j_msg = [130, 187].pack('C*').force_encoding(Encoding::Windows_31J)
82
+ utf_8_msg = "\u305d"
83
+
84
+ $stdout.expects(:puts).with("\e[mNotice: #{utf_8_msg}: #{mixed_utf8}\e[0m")
85
+
86
+ Puppet::Util::Log.new(:level => :notice, :message => mixed_utf8, :source => win_31j_msg)
87
+ expect(logs.last.source).to eq(utf_8_msg)
88
+ end
89
+
61
90
  describe ".setup_default" do
62
91
  it "should default to :syslog" do
63
92
  Puppet.features.stubs(:syslog?).returns(true)
@@ -4,6 +4,10 @@
4
4
  require 'spec_helper'
5
5
 
6
6
  describe "FFI::MemoryPointer", :if => Puppet.features.microsoft_windows? do
7
+ # use 2 bad bytes at end so we have even number of bytes / characters
8
+ let (:bad_string) { "hello invalid world".encode(Encoding::UTF_16LE) + "\xDD\xDD".force_encoding(Encoding::UTF_16LE) }
9
+ let (:bad_string_bytes) { bad_string.bytes.to_a }
10
+
7
11
  context "read_wide_string" do
8
12
  let (:string) { "foo_bar" }
9
13
 
@@ -24,6 +28,40 @@ describe "FFI::MemoryPointer", :if => Puppet.features.microsoft_windows? do
24
28
 
25
29
  expect(read_string.encoding).to eq(Encoding::UTF_8)
26
30
  end
31
+
32
+ it "should raise an error and emit a debug message when receiving a string containing invalid bytes in the destination encoding" do
33
+ # enable a debug output sink to local string array
34
+ Puppet.debug = true
35
+ arraydest = []
36
+ Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(arraydest))
37
+
38
+ read_string = nil
39
+
40
+ expect {
41
+ FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr|
42
+ # uchar here is synonymous with byte
43
+ ptr.put_array_of_uchar(0, bad_string_bytes)
44
+
45
+ read_string = ptr.read_wide_string(bad_string.length)
46
+ end
47
+ }.to raise_error(Encoding::InvalidByteSequenceError)
48
+
49
+ expect(read_string).to be_nil
50
+ expect(arraydest.last.message).to eq("Unable to convert value #{bad_string.dump} to encoding UTF-8 due to #<Encoding::InvalidByteSequenceError: \"\\xDD\\xDD\" on UTF-16LE>")
51
+ end
52
+
53
+ it "should not raise an error when receiving a string containing invalid bytes in the destination encoding, when specifying :invalid => :replace" do
54
+ read_string = nil
55
+
56
+ FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr|
57
+ # uchar here is synonymous with byte
58
+ ptr.put_array_of_uchar(0, bad_string_bytes)
59
+
60
+ read_string = ptr.read_wide_string(bad_string.length, Encoding::UTF_8, :invalid => :replace)
61
+ end
62
+
63
+ expect(read_string).to eq("hello invalid world\uFFFD")
64
+ end
27
65
  end
28
66
 
29
67
  context "read_arbitrary_wide_string_up_to" do
@@ -66,5 +104,18 @@ describe "FFI::MemoryPointer", :if => Puppet.features.microsoft_windows? do
66
104
 
67
105
  expect(read_string.encoding).to eq(Encoding::UTF_8)
68
106
  end
107
+
108
+ it "should not raise an error when receiving a string containing invalid bytes in the destination encoding, when specifying :invalid => :replace" do
109
+ read_string = nil
110
+
111
+ FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr|
112
+ # uchar here is synonymous with byte
113
+ ptr.put_array_of_uchar(0, bad_string_bytes)
114
+
115
+ read_string = ptr.read_arbitrary_wide_string_up_to(ptr.size / 2, :single_null, :invalid => :replace)
116
+ end
117
+
118
+ expect(read_string).to eq("hello invalid world\uFFFD")
119
+ end
69
120
  end
70
121
  end