puppet-lint 2.3.6 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +238 -87
- data/README.md +18 -0
- data/lib/puppet-lint.rb +1 -1
- data/lib/puppet-lint/data.rb +26 -11
- data/lib/puppet-lint/lexer.rb +97 -200
- data/lib/puppet-lint/lexer/string_slurper.rb +173 -0
- data/lib/puppet-lint/lexer/token.rb +8 -0
- data/lib/puppet-lint/optparser.rb +4 -5
- data/lib/puppet-lint/plugins/check_classes/parameter_order.rb +12 -1
- data/lib/puppet-lint/plugins/check_conditionals/case_without_default.rb +15 -1
- data/lib/puppet-lint/plugins/check_documentation/documentation.rb +4 -0
- data/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb +5 -2
- data/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb +1 -0
- data/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb +71 -0
- data/lib/puppet-lint/plugins/check_whitespace/arrow_alignment.rb +1 -1
- data/lib/puppet-lint/tasks/puppet-lint.rb +14 -0
- data/lib/puppet-lint/tasks/release_test.rb +3 -1
- data/lib/puppet-lint/version.rb +1 -1
- data/spec/fixtures/test/manifests/two_warnings.pp +5 -0
- data/spec/puppet-lint/bin_spec.rb +47 -6
- data/spec/puppet-lint/data_spec.rb +12 -0
- data/spec/puppet-lint/lexer/string_slurper_spec.rb +473 -0
- data/spec/puppet-lint/lexer_spec.rb +1153 -590
- data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +18 -0
- data/spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb +15 -1
- data/spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb +39 -0
- data/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb +18 -0
- data/spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb +16 -0
- data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +5 -5
- data/spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb +6 -6
- data/spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb +32 -0
- data/spec/puppet-lint/plugins/check_variables/variable_is_lowercase_spec.rb +28 -0
- data/spec/spec_helper.rb +7 -5
- metadata +14 -17
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.rubocop.yml +0 -74
- data/.rubocop_todo.yml +0 -89
- data/.travis.yml +0 -24
- data/Gemfile +0 -40
- data/Rakefile +0 -42
- data/appveyor.yml +0 -33
- data/puppet-lint.gemspec +0 -19
@@ -55,7 +55,9 @@ def with_puppet_lint_head
|
|
55
55
|
end
|
56
56
|
|
57
57
|
task :release_test do
|
58
|
-
branch = if ENV['
|
58
|
+
branch = if ENV['GITHUB_REF']
|
59
|
+
ENV['GITHUB_REF']
|
60
|
+
elsif ENV['APPVEYOR']
|
59
61
|
ENV['APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH']
|
60
62
|
elsif ENV['TRAVIS']
|
61
63
|
ENV['TRAVIS_PULL_REQUEST_BRANCH']
|
data/lib/puppet-lint/version.rb
CHANGED
@@ -91,8 +91,8 @@ describe PuppetLint::Bin do
|
|
91
91
|
its(:stdout) do
|
92
92
|
is_expected.to eq(
|
93
93
|
[
|
94
|
-
"#{args[0]} - WARNING: optional parameter listed before required parameter on line 2",
|
95
|
-
"#{args[1]} - ERROR: test::foo not in autoload module layout on line 2",
|
94
|
+
"#{args[0]} - WARNING: optional parameter listed before required parameter on line 2 (check: parameter_order)",
|
95
|
+
"#{args[1]} - ERROR: test::foo not in autoload module layout on line 2 (check: autoloader_layout)",
|
96
96
|
].join("\n")
|
97
97
|
)
|
98
98
|
end
|
@@ -102,7 +102,7 @@ describe PuppetLint::Bin do
|
|
102
102
|
let(:args) { 'spec/fixtures/test/manifests/malformed.pp' }
|
103
103
|
|
104
104
|
its(:exitstatus) { is_expected.to eq(1) }
|
105
|
-
its(:stdout) { is_expected.to eq('ERROR: Syntax error on line 1') }
|
105
|
+
its(:stdout) { is_expected.to eq('ERROR: Syntax error on line 1 (check: syntax)') }
|
106
106
|
its(:stderr) { is_expected.to eq('Try running `puppet parser validate <file>`') }
|
107
107
|
end
|
108
108
|
|
@@ -198,7 +198,7 @@ describe PuppetLint::Bin do
|
|
198
198
|
its(:stdout) do
|
199
199
|
is_expected.to eq(
|
200
200
|
[
|
201
|
-
'WARNING: optional parameter listed before required parameter on line 2',
|
201
|
+
'WARNING: optional parameter listed before required parameter on line 2 (check: parameter_order)',
|
202
202
|
'',
|
203
203
|
" define test::warning($foo='bar', $baz) { }",
|
204
204
|
' ^',
|
@@ -432,7 +432,7 @@ describe PuppetLint::Bin do
|
|
432
432
|
its(:stdout) do
|
433
433
|
is_expected.to eq(
|
434
434
|
[
|
435
|
-
'IGNORED: double quoted string containing no variables on line 3',
|
435
|
+
'IGNORED: double quoted string containing no variables on line 3 (check: double_quoted_strings)',
|
436
436
|
' for a good reason',
|
437
437
|
].join("\n")
|
438
438
|
)
|
@@ -457,7 +457,7 @@ describe PuppetLint::Bin do
|
|
457
457
|
end
|
458
458
|
|
459
459
|
its(:exitstatus) { is_expected.to eq(0) }
|
460
|
-
its(:stdout) { is_expected.to match(%r{^.*line 6
|
460
|
+
its(:stdout) { is_expected.to match(%r{^.*line 6(?!\d)}) }
|
461
461
|
end
|
462
462
|
|
463
463
|
context 'when an lint:endignore control comment exists with no opening lint:ignore comment' do
|
@@ -526,4 +526,45 @@ describe PuppetLint::Bin do
|
|
526
526
|
end
|
527
527
|
end
|
528
528
|
end
|
529
|
+
|
530
|
+
context 'when overriding config file options with command line options' do
|
531
|
+
context 'and config file sets "--only-checks=variable_contains_dash"' do
|
532
|
+
around(:context) do |example|
|
533
|
+
Dir.mktmpdir do |tmpdir|
|
534
|
+
Dir.chdir(tmpdir) do
|
535
|
+
File.open('.puppet-lint.rc', 'wb') do |f|
|
536
|
+
f.puts('--only-checks=variable_contains_dash')
|
537
|
+
end
|
538
|
+
|
539
|
+
example.run
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
context 'and command-line does not override "--only-checks"' do
|
545
|
+
let(:args) do
|
546
|
+
File.join(File.dirname(__FILE__), '..', 'fixtures', 'test', 'manifests', 'two_warnings.pp')
|
547
|
+
end
|
548
|
+
|
549
|
+
its(:exitstatus) { is_expected.to eq(0) }
|
550
|
+
its(:stdout) do
|
551
|
+
is_expected.to eq('WARNING: variable contains a dash on line 3 (check: variable_contains_dash)')
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
context 'and command-line sets "--only-checks=variable_is_lowercase"' do
|
556
|
+
let(:args) do
|
557
|
+
[
|
558
|
+
'--only-checks=variable_is_lowercase',
|
559
|
+
File.join(File.dirname(__FILE__), '..', 'fixtures', 'test', 'manifests', 'two_warnings.pp'),
|
560
|
+
]
|
561
|
+
end
|
562
|
+
|
563
|
+
its(:exitstatus) { is_expected.to eq(0) }
|
564
|
+
its(:stdout) do
|
565
|
+
is_expected.to eq('WARNING: variable contains an uppercase letter on line 4 (check: variable_is_lowercase)')
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
569
|
+
end
|
529
570
|
end
|
@@ -20,6 +20,18 @@ describe PuppetLint::Data do
|
|
20
20
|
}
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
context 'when typo in namespace separator makes parser look for resource' do
|
25
|
+
let(:manifest) { '$testparam = $::module:;testparam' }
|
26
|
+
|
27
|
+
it 'raises a SyntaxError' do
|
28
|
+
expect {
|
29
|
+
data.resource_indexes
|
30
|
+
}.to raise_error(PuppetLint::SyntaxError) { |error|
|
31
|
+
expect(error.token).to eq(data.tokens[5])
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
23
35
|
end
|
24
36
|
|
25
37
|
describe '.insert' do
|
@@ -0,0 +1,473 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe PuppetLint::Lexer::StringSlurper do
|
6
|
+
describe '#parse' do
|
7
|
+
subject(:segments) { described_class.new(string).parse }
|
8
|
+
|
9
|
+
context 'when parsing an unterminated string' do
|
10
|
+
let(:string) { 'foo' }
|
11
|
+
|
12
|
+
it 'raises an UnterminatedStringError' do
|
13
|
+
expect { segments }.to raise_error(described_class::UnterminatedStringError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when parsing up to a double quote' do
|
18
|
+
let(:string) { 'foo"bar' }
|
19
|
+
|
20
|
+
it 'returns a single segment up to the double quote' do
|
21
|
+
expect(segments).to eq([[:STRING, 'foo']])
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'and the string is empty' do
|
25
|
+
let(:string) { '"' }
|
26
|
+
|
27
|
+
it 'returns a single empty string segment' do
|
28
|
+
expect(segments).to eq([[:STRING, '']])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'and the string contains' do
|
33
|
+
context 'a newline' do
|
34
|
+
let(:string) { %(foo\nbar") }
|
35
|
+
|
36
|
+
it 'includes the newline in the string segment' do
|
37
|
+
expect(segments).to eq([[:STRING, "foo\nbar"]])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'an escaped $var' do
|
42
|
+
let(:string) { '\$foo"' }
|
43
|
+
|
44
|
+
it 'does not create an unenclosed variable segment' do
|
45
|
+
expect(segments).to eq([[:STRING, '\$foo']])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'an escaped ${} enclosure' do
|
50
|
+
let(:string) { '\"\${\"string\"}\""' }
|
51
|
+
|
52
|
+
it 'does not create an interpolation segment' do
|
53
|
+
expect(segments).to eq([[:STRING, '\"\${\"string\"}\"']])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'a variable and a suffix' do
|
58
|
+
let(:string) { '${foo}bar"' }
|
59
|
+
|
60
|
+
it 'puts the variable into an interpolation segment' do
|
61
|
+
expect(segments).to eq([
|
62
|
+
[:STRING, ''],
|
63
|
+
[:INTERP, 'foo'],
|
64
|
+
[:STRING, 'bar'],
|
65
|
+
])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'a variable surrounded by text' do
|
70
|
+
let(:string) { 'foo${bar}baz"' }
|
71
|
+
|
72
|
+
it 'puts the variable into an interpolation segment' do
|
73
|
+
expect(segments).to eq([
|
74
|
+
[:STRING, 'foo'],
|
75
|
+
[:INTERP, 'bar'],
|
76
|
+
[:STRING, 'baz'],
|
77
|
+
])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'multiple variables with surrounding text' do
|
82
|
+
let(:string) { 'foo${bar}baz${gronk}meh"' }
|
83
|
+
|
84
|
+
it 'puts each variable into an interpolation segment' do
|
85
|
+
expect(segments).to eq([
|
86
|
+
[:STRING, 'foo'],
|
87
|
+
[:INTERP, 'bar'],
|
88
|
+
[:STRING, 'baz'],
|
89
|
+
[:INTERP, 'gronk'],
|
90
|
+
[:STRING, 'meh'],
|
91
|
+
])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'only an enclosed variable' do
|
96
|
+
let(:string) { '${bar}"' }
|
97
|
+
|
98
|
+
it 'puts empty string segments around the interpolated segment' do
|
99
|
+
expect(segments).to eq([
|
100
|
+
[:STRING, ''],
|
101
|
+
[:INTERP, 'bar'],
|
102
|
+
[:STRING, ''],
|
103
|
+
])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'an enclosed variable with an unnecessary $' do
|
108
|
+
let(:string) { '${$bar}"' }
|
109
|
+
|
110
|
+
it 'does not remove the unnecessary $' do
|
111
|
+
expect(segments).to eq([
|
112
|
+
[:STRING, ''],
|
113
|
+
[:INTERP, '$bar'],
|
114
|
+
[:STRING, ''],
|
115
|
+
])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'a variable with an array reference' do
|
120
|
+
let(:string) { '${foo[bar][baz]}"' }
|
121
|
+
|
122
|
+
it 'includes the references in the interpolated section' do
|
123
|
+
expect(segments).to eq([
|
124
|
+
[:STRING, ''],
|
125
|
+
[:INTERP, 'foo[bar][baz]'],
|
126
|
+
[:STRING, ''],
|
127
|
+
])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'only enclosed variables' do
|
132
|
+
let(:string) { '${foo}${bar}"' }
|
133
|
+
|
134
|
+
it 'creates an interpolation section per variable' do
|
135
|
+
expect(segments).to eq([
|
136
|
+
[:STRING, ''],
|
137
|
+
[:INTERP, 'foo'],
|
138
|
+
[:STRING, ''],
|
139
|
+
[:INTERP, 'bar'],
|
140
|
+
[:STRING, ''],
|
141
|
+
])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'an unenclosed variable' do
|
146
|
+
let(:string) { '$foo"' }
|
147
|
+
|
148
|
+
it 'creates a special segment for the unenclosed variable' do
|
149
|
+
expect(segments).to eq([
|
150
|
+
[:STRING, ''],
|
151
|
+
[:UNENC_VAR, '$foo'],
|
152
|
+
[:STRING, ''],
|
153
|
+
])
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'an interpolation with a nested single quoted string' do
|
158
|
+
let(:string) { %(string with ${'a nested single quoted string'} inside it") }
|
159
|
+
|
160
|
+
it 'creates an interpolation segment for the nested string' do
|
161
|
+
expect(segments).to eq([
|
162
|
+
[:STRING, 'string with '],
|
163
|
+
[:INTERP, "'a nested single quoted string'"],
|
164
|
+
[:STRING, ' inside it'],
|
165
|
+
])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'an interpolation with nested math' do
|
170
|
+
let(:string) { 'string with ${(3+5)/4} nested math"' }
|
171
|
+
|
172
|
+
it 'creates an interpolation segment for the nested math' do
|
173
|
+
expect(segments).to eq([
|
174
|
+
[:STRING, 'string with '],
|
175
|
+
[:INTERP, '(3+5)/4'],
|
176
|
+
[:STRING, ' nested math'],
|
177
|
+
])
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'an interpolation with a nested array' do
|
182
|
+
let(:string) { %(string with ${['an array ', $v2]} in it") }
|
183
|
+
|
184
|
+
it 'creates an interpolation segment for the nested array' do
|
185
|
+
expect(segments).to eq([
|
186
|
+
[:STRING, 'string with '],
|
187
|
+
[:INTERP, "['an array ', $v2]"],
|
188
|
+
[:STRING, ' in it'],
|
189
|
+
])
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'repeated $s' do
|
194
|
+
let(:string) { '$$$$"' }
|
195
|
+
|
196
|
+
it 'creates a single string segment' do
|
197
|
+
expect(segments).to eq([[:STRING, '$$$$']])
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'multiple unenclosed variables' do
|
202
|
+
let(:string) { '$foo$bar"' }
|
203
|
+
|
204
|
+
it 'creates a special segment for each unenclosed variable' do
|
205
|
+
expect(segments).to eq([
|
206
|
+
[:STRING, ''],
|
207
|
+
[:UNENC_VAR, '$foo'],
|
208
|
+
[:STRING, ''],
|
209
|
+
[:UNENC_VAR, '$bar'],
|
210
|
+
[:STRING, ''],
|
211
|
+
])
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'an unenclosed variable with a trailing $' do
|
216
|
+
let(:string) { 'foo$bar$"' }
|
217
|
+
|
218
|
+
it 'places the trailing $ in a string segment' do
|
219
|
+
expect(segments).to eq([
|
220
|
+
[:STRING, 'foo'],
|
221
|
+
[:UNENC_VAR, '$bar'],
|
222
|
+
[:STRING, '$'],
|
223
|
+
])
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context 'an unenclosed variable starting with two $s' do
|
228
|
+
let(:string) { 'foo$$bar"' }
|
229
|
+
|
230
|
+
it 'includes the preceeding $ in the string segment before the unenclosed variable' do
|
231
|
+
expect(segments).to eq([
|
232
|
+
[:STRING, 'foo$'],
|
233
|
+
[:UNENC_VAR, '$bar'],
|
234
|
+
[:STRING, ''],
|
235
|
+
])
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context 'an unenclosed variable with incorrect namespacing' do
|
240
|
+
let(:string) { '$foo::::bar"' }
|
241
|
+
|
242
|
+
it 'only includes the valid part of the variable name in the segment' do
|
243
|
+
expect(segments).to eq([
|
244
|
+
[:STRING, ''],
|
245
|
+
[:UNENC_VAR, '$foo'],
|
246
|
+
[:STRING, '::::bar'],
|
247
|
+
])
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context 'a variable followed by an odd number of backslashes before a double quote' do
|
252
|
+
let(:string) { '${foo}\"bar"' }
|
253
|
+
|
254
|
+
it 'does not let this double quote terminate the string' do
|
255
|
+
expect(segments).to eq([
|
256
|
+
[:STRING, ''],
|
257
|
+
[:INTERP, 'foo'],
|
258
|
+
[:STRING, '\\"bar'],
|
259
|
+
])
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context 'a variable followed by an even number of backslashes before a double quote' do
|
264
|
+
let(:string) { '${foo}\\\\"bar"' }
|
265
|
+
|
266
|
+
it 'recognizes this double quote as the terminator' do
|
267
|
+
expect(segments).to eq([
|
268
|
+
[:STRING, ''],
|
269
|
+
[:INTERP, 'foo'],
|
270
|
+
[:STRING, '\\\\'],
|
271
|
+
])
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
context 'an interpolation with a complex function chain' do
|
276
|
+
let(:string) { '${key} ${flatten([$value]).join("\nkey ")}"' }
|
277
|
+
|
278
|
+
it 'keeps the whole function chain in a single interpolation segment' do
|
279
|
+
expect(segments).to eq([
|
280
|
+
[:STRING, ''],
|
281
|
+
[:INTERP, 'key'],
|
282
|
+
[:STRING, ' '],
|
283
|
+
[:INTERP, 'flatten([$value]).join("\nkey ")'],
|
284
|
+
[:STRING, ''],
|
285
|
+
])
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'nested interpolations' do
|
290
|
+
let(:string) { '${facts["network_${iface}"]}/${facts["netmask_${iface}"]}"' }
|
291
|
+
|
292
|
+
it 'keeps each full interpolation in its own segment' do
|
293
|
+
expect(segments).to eq([
|
294
|
+
[:STRING, ''],
|
295
|
+
[:INTERP, 'facts["network_${iface}"]'],
|
296
|
+
[:STRING, '/'],
|
297
|
+
[:INTERP, 'facts["netmask_${iface}"]'],
|
298
|
+
[:STRING, ''],
|
299
|
+
])
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
context 'interpolation with nested braces' do
|
304
|
+
let(:string) { '${$foo.map |$bar| { something($bar) }}"' }
|
305
|
+
|
306
|
+
it do
|
307
|
+
expect(segments).to eq([
|
308
|
+
[:STRING, ''],
|
309
|
+
[:INTERP, '$foo.map |$bar| { something($bar) }'],
|
310
|
+
[:STRING, ''],
|
311
|
+
])
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe '#parse_heredoc' do
|
319
|
+
subject(:segments) { described_class.new(heredoc).parse_heredoc(heredoc_tag) }
|
320
|
+
|
321
|
+
context 'when the heredoc text contains the tag' do
|
322
|
+
let(:heredoc) { %( SOMETHING else\n |-THING) }
|
323
|
+
let(:heredoc_tag) { 'THING' }
|
324
|
+
|
325
|
+
it 'terminates the heredoc at the closing tag' do
|
326
|
+
expect(segments).to eq([
|
327
|
+
[:HEREDOC, " SOMETHING else\n "],
|
328
|
+
[:HEREDOC_TERM, '|-THING'],
|
329
|
+
])
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
context 'when parsing a heredoc with interpolation disabled' do
|
334
|
+
context 'that is a plain heredoc' do
|
335
|
+
let(:heredoc) { %( SOMETHING\n ELSE\n :\n |-myheredoc) }
|
336
|
+
let(:heredoc_tag) { 'myheredoc' }
|
337
|
+
|
338
|
+
it 'splits the heredoc into two segments' do
|
339
|
+
expect(segments).to eq([
|
340
|
+
[:HEREDOC, " SOMETHING\n ELSE\n :\n "],
|
341
|
+
[:HEREDOC_TERM, '|-myheredoc'],
|
342
|
+
])
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
context 'that contains a value enclosed in ${}' do
|
347
|
+
let(:heredoc) { %( SOMETHING\n ${else}\n :\n |-myheredoc) }
|
348
|
+
let(:heredoc_tag) { 'myheredoc' }
|
349
|
+
|
350
|
+
it 'does not create an interpolation segment' do
|
351
|
+
expect(segments).to eq([
|
352
|
+
[:HEREDOC, " SOMETHING\n ${else}\n :\n "],
|
353
|
+
[:HEREDOC_TERM, '|-myheredoc'],
|
354
|
+
])
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
context 'that contains an unenclosed variable' do
|
359
|
+
let(:heredoc) { %( SOMETHING\n $else\n :\n |-myheredoc) }
|
360
|
+
let(:heredoc_tag) { 'myheredoc' }
|
361
|
+
|
362
|
+
it 'does not create a segment for the unenclosed variable' do
|
363
|
+
expect(segments).to eq([
|
364
|
+
[:HEREDOC, " SOMETHING\n $else\n :\n "],
|
365
|
+
[:HEREDOC_TERM, '|-myheredoc'],
|
366
|
+
])
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
context 'when parsing a heredoc with interpolation enabled' do
|
372
|
+
context 'that is a plain heredoc' do
|
373
|
+
let(:heredoc) { %( SOMETHING\n ELSE\n :\n |-myheredoc) }
|
374
|
+
let(:heredoc_tag) { '"myheredoc"' }
|
375
|
+
|
376
|
+
it 'splits the heredoc into two segments' do
|
377
|
+
expect(segments).to eq([
|
378
|
+
[:HEREDOC, " SOMETHING\n ELSE\n :\n "],
|
379
|
+
[:HEREDOC_TERM, '|-myheredoc'],
|
380
|
+
])
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
context 'that contains a value enclosed in ${}' do
|
385
|
+
let(:heredoc) { %( SOMETHING\n ${else}\n :\n |-myheredoc) }
|
386
|
+
let(:heredoc_tag) { '"myheredoc"' }
|
387
|
+
|
388
|
+
it 'creates an interpolation segment' do
|
389
|
+
expect(segments).to eq([
|
390
|
+
[:HEREDOC, " SOMETHING\n "],
|
391
|
+
[:INTERP, 'else'],
|
392
|
+
[:HEREDOC, "\n :\n "],
|
393
|
+
[:HEREDOC_TERM, '|-myheredoc'],
|
394
|
+
])
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context 'that contains an unenclosed variable' do
|
399
|
+
let(:heredoc) { %( SOMETHING\n $else\n :\n |-myheredoc) }
|
400
|
+
let(:heredoc_tag) { '"myheredoc"' }
|
401
|
+
|
402
|
+
it 'does not create a segment for the unenclosed variable' do
|
403
|
+
expect(segments).to eq([
|
404
|
+
[:HEREDOC, " SOMETHING\n "],
|
405
|
+
[:UNENC_VAR, '$else'],
|
406
|
+
[:HEREDOC, "\n :\n "],
|
407
|
+
[:HEREDOC_TERM, '|-myheredoc'],
|
408
|
+
])
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
context 'that contains a nested interpolation' do
|
413
|
+
let(:heredoc) { %( SOMETHING\n ${facts["other_${thing}"]}\n :\n |-myheredoc) }
|
414
|
+
let(:heredoc_tag) { '"myheredoc"' }
|
415
|
+
|
416
|
+
it 'does not create a segment for the unenclosed variable' do
|
417
|
+
expect(segments).to eq([
|
418
|
+
[:HEREDOC, " SOMETHING\n "],
|
419
|
+
[:INTERP, 'facts["other_${thing}"]'],
|
420
|
+
[:HEREDOC, "\n :\n "],
|
421
|
+
[:HEREDOC_TERM, '|-myheredoc'],
|
422
|
+
])
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context 'that contains an interpolation with nested braces' do
|
427
|
+
let(:heredoc) { %( SOMETHING\n ${$foo.map |$bar| { something($bar) }}\n :\n |-myheredoc) }
|
428
|
+
let(:heredoc_tag) { '"myheredoc"' }
|
429
|
+
|
430
|
+
it 'does not create a segment for the unenclosed variable' do
|
431
|
+
expect(segments).to eq([
|
432
|
+
[:HEREDOC, " SOMETHING\n "],
|
433
|
+
[:INTERP, '$foo.map |$bar| { something($bar) }'],
|
434
|
+
[:HEREDOC, "\n :\n "],
|
435
|
+
[:HEREDOC_TERM, '|-myheredoc'],
|
436
|
+
])
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
context 'that contains braces' do
|
441
|
+
let(:heredoc) { %( {\n "foo": "bar"\n }\n |-end) }
|
442
|
+
let(:heredoc_tag) { '"end":json/' }
|
443
|
+
|
444
|
+
it do
|
445
|
+
expect(segments).to eq([
|
446
|
+
[:HEREDOC, %( {\n "foo": "bar"\n }\n )],
|
447
|
+
[:HEREDOC_TERM, '|-end'],
|
448
|
+
])
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
describe '#consumed_chars' do
|
455
|
+
subject { described_class.new(string).tap(&:parse).consumed_chars }
|
456
|
+
|
457
|
+
context 'when slurping a string containing multibyte characters' do
|
458
|
+
let(:string) { 'accentués"' }
|
459
|
+
|
460
|
+
it 'counts the multibyte character as a single consumed character' do
|
461
|
+
is_expected.to eq(10)
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
context 'when slurping an empty string' do
|
466
|
+
let(:string) { '"' }
|
467
|
+
|
468
|
+
it 'consumes only the closing quote' do
|
469
|
+
is_expected.to eq(1)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|