puppet-lint 2.4.0 → 2.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1014
- data/HISTORY.md +1130 -0
- data/lib/puppet-lint/data.rb +9 -9
- data/lib/puppet-lint/lexer/string_slurper.rb +21 -5
- data/lib/puppet-lint/lexer/token.rb +6 -0
- data/lib/puppet-lint/lexer.rb +16 -5
- data/lib/puppet-lint/plugins/check_classes/parameter_order.rb +12 -1
- data/lib/puppet-lint/plugins/check_documentation/documentation.rb +4 -0
- data/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb +1 -1
- data/lib/puppet-lint/plugins/check_strings/double_quoted_strings.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/lib/puppet-lint.rb +1 -1
- data/spec/puppet-lint/bin_spec.rb +8 -8
- data/spec/puppet-lint/lexer/string_slurper_spec.rb +68 -2
- data/spec/puppet-lint/lexer_spec.rb +56 -0
- data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +18 -0
- data/spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb +14 -0
- data/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb +18 -0
- data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +6 -5
- data/spec/puppet-lint/plugins/check_variables/variable_is_lowercase_spec.rb +28 -0
- data/spec/spec_helper.rb +7 -5
- metadata +16 -18
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.rubocop.yml +0 -74
- data/.rubocop_todo.yml +0 -89
- data/.travis.yml +0 -26
- data/Gemfile +0 -40
- data/Rakefile +0 -42
- data/appveyor.yml +0 -35
- data/puppet-lint.gemspec +0 -19
data/lib/puppet-lint/data.rb
CHANGED
@@ -171,21 +171,21 @@ class PuppetLint::Data
|
|
171
171
|
tokens.select { |t| t.type == :COLON }.each do |colon_token|
|
172
172
|
next unless colon_token.next_code_token && colon_token.next_code_token.type != :LBRACE
|
173
173
|
|
174
|
-
|
175
|
-
|
174
|
+
rel_start_idx = tokens[marker..-1].index(colon_token)
|
175
|
+
break if rel_start_idx.nil?
|
176
|
+
start_idx = rel_start_idx + marker
|
176
177
|
end_token = colon_token.next_token_of([:SEMIC, :RBRACE])
|
177
|
-
|
178
|
-
|
179
|
-
|
178
|
+
rel_end_idx = tokens[start_idx..-1].index(end_token)
|
179
|
+
raise PuppetLint::SyntaxError, colon_token if rel_end_idx.nil?
|
180
|
+
marker = rel_end_idx + start_idx
|
180
181
|
|
181
182
|
result << {
|
182
183
|
:start => start_idx + 1,
|
183
|
-
:end =>
|
184
|
-
:tokens => tokens[start_idx..
|
184
|
+
:end => marker,
|
185
|
+
:tokens => tokens[start_idx..marker],
|
185
186
|
:type => find_resource_type_token(start_idx),
|
186
|
-
:param_tokens => find_resource_param_tokens(tokens[start_idx..
|
187
|
+
:param_tokens => find_resource_param_tokens(tokens[start_idx..marker]),
|
187
188
|
}
|
188
|
-
marker = end_idx
|
189
189
|
end
|
190
190
|
result
|
191
191
|
end
|
@@ -37,11 +37,11 @@ class PuppetLint
|
|
37
37
|
end_interp
|
38
38
|
elsif unenclosed_variable?
|
39
39
|
unenclosed_variable
|
40
|
-
elsif scanner.match?(ESC_DQUOTE_PATTERN)
|
41
|
-
@segment << scanner.scan(ESC_DQUOTE_PATTERN)
|
42
40
|
elsif scanner.match?(END_STRING_PATTERN)
|
43
41
|
end_string
|
44
42
|
break if interp_stack.empty?
|
43
|
+
elsif scanner.match?(ESC_DQUOTE_PATTERN)
|
44
|
+
@segment << scanner.scan(ESC_DQUOTE_PATTERN)
|
45
45
|
else
|
46
46
|
read_char
|
47
47
|
end
|
@@ -60,7 +60,7 @@ class PuppetLint
|
|
60
60
|
|
61
61
|
def parse_heredoc(heredoc_tag)
|
62
62
|
heredoc_name = heredoc_tag[%r{\A"?(.+?)"?(:.+?)?#{PuppetLint::Lexer::WHITESPACE_RE}*(/.*)?\Z}, 1]
|
63
|
-
end_heredoc_pattern = %r{
|
63
|
+
end_heredoc_pattern = %r{^\|?\s*-?\s*#{Regexp.escape(heredoc_name)}$}
|
64
64
|
interpolation = heredoc_tag.start_with?('"')
|
65
65
|
|
66
66
|
@segment_type = :HEREDOC
|
@@ -98,11 +98,27 @@ class PuppetLint
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
|
102
|
-
|
101
|
+
# Get the number of characters consumed by the StringSlurper.
|
102
|
+
#
|
103
|
+
# StringScanner from Ruby 2.0 onwards supports #charpos which returns
|
104
|
+
# the number of characters and is multibyte character aware.
|
105
|
+
#
|
106
|
+
# Prior to this, Ruby's multibyte character support in Strings was a
|
107
|
+
# bit unusual and neither String#length nor String#split behave as
|
108
|
+
# expected, so we use String#scan to split all the consumed text using
|
109
|
+
# a UTF-8 aware regex and use the length of the result
|
110
|
+
def consumed_chars
|
111
|
+
return scanner.charpos if scanner.respond_to?(:charpos)
|
112
|
+
|
113
|
+
(scanner.pre_match + scanner.matched).scan(%r{.}mu).length
|
103
114
|
end
|
104
115
|
|
105
116
|
def start_interp
|
117
|
+
if @segment.last && @segment.last == '\\'
|
118
|
+
read_char
|
119
|
+
return
|
120
|
+
end
|
121
|
+
|
106
122
|
if interp_stack.empty?
|
107
123
|
scanner.skip(START_INTERP_PATTERN)
|
108
124
|
results << [@segment_type, @segment.join]
|
@@ -199,6 +199,12 @@ class PuppetLint
|
|
199
199
|
end
|
200
200
|
nil
|
201
201
|
end
|
202
|
+
|
203
|
+
def interpolated_variable?
|
204
|
+
return false if type == :TYPE && value != 'type'
|
205
|
+
return true if type == :NAME
|
206
|
+
PuppetLint::Lexer::KEYWORDS.include?(type.to_s.downcase)
|
207
|
+
end
|
202
208
|
end
|
203
209
|
end
|
204
210
|
end
|
data/lib/puppet-lint/lexer.rb
CHANGED
@@ -104,6 +104,7 @@ class PuppetLint
|
|
104
104
|
:IF => true,
|
105
105
|
:ELSIF => true,
|
106
106
|
:LPAREN => true,
|
107
|
+
:EQUALS => true,
|
107
108
|
}.freeze
|
108
109
|
|
109
110
|
# Internal: some commonly used regular expressions
|
@@ -123,6 +124,8 @@ class PuppetLint
|
|
123
124
|
# value of the token.
|
124
125
|
KNOWN_TOKENS = [
|
125
126
|
[:WHITESPACE, %r{\A(#{WHITESPACE_RE}+)}],
|
127
|
+
# FIXME: Future breaking change, the following :TYPE tokens conflict with
|
128
|
+
# the :TYPE keyword token.
|
126
129
|
[:TYPE, %r{\A(Integer|Float|Boolean|Regexp|String|Array|Hash|Resource|Class|Collection|Scalar|Numeric|CatalogEntry|Data|Tuple|Struct|Optional|NotUndef|Variant|Enum|Pattern|Any|Callable|Type|Runtime|Undef|Default|Sensitive)\b}], # rubocop:disable Metrics/LineLength
|
127
130
|
[:CLASSREF, %r{\A(((::){0,1}[A-Z][-\w]*)+)}],
|
128
131
|
[:NUMBER, %r{\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b}],
|
@@ -239,9 +242,9 @@ class PuppetLint
|
|
239
242
|
begin
|
240
243
|
string_segments = slurper.parse
|
241
244
|
process_string_segments(string_segments)
|
242
|
-
length = slurper.
|
245
|
+
length = slurper.consumed_chars + 1
|
243
246
|
rescue PuppetLint::Lexer::StringSlurper::UnterminatedStringError
|
244
|
-
raise PuppetLint::LexerError
|
247
|
+
raise PuppetLint::LexerError.new(@line_no, @column, 'unterminated string')
|
245
248
|
end
|
246
249
|
|
247
250
|
elsif heredoc_name = chunk[%r{\A@\(("?.+?"?(:.+?)?#{WHITESPACE_RE}*(/.*?)?)\)}, 1]
|
@@ -286,13 +289,21 @@ class PuppetLint
|
|
286
289
|
slurper = PuppetLint::Lexer::StringSlurper.new(code[i + length..-1])
|
287
290
|
heredoc_segments = slurper.parse_heredoc(heredoc_tag)
|
288
291
|
process_heredoc_segments(heredoc_segments)
|
289
|
-
length += slurper.
|
292
|
+
length += slurper.consumed_chars
|
290
293
|
end
|
291
294
|
|
292
295
|
elsif eol = chunk[%r{\A(#{LINE_END_RE})}, 1]
|
293
296
|
length = eol.size
|
294
297
|
tokens << new_token(:NEWLINE, eol)
|
295
298
|
|
299
|
+
unless heredoc_queue.empty?
|
300
|
+
heredoc_tag = heredoc_queue.shift
|
301
|
+
slurper = PuppetLint::Lexer::StringSlurper.new(code[i + length..-1])
|
302
|
+
heredoc_segments = slurper.parse_heredoc(heredoc_tag)
|
303
|
+
process_heredoc_segments(heredoc_segments)
|
304
|
+
length += slurper.consumed_chars
|
305
|
+
end
|
306
|
+
|
296
307
|
elsif chunk.start_with?('/')
|
297
308
|
length = 1
|
298
309
|
tokens << new_token(:DIV, '/')
|
@@ -408,7 +419,7 @@ class PuppetLint
|
|
408
419
|
lexer = PuppetLint::Lexer.new
|
409
420
|
lexer.tokenise(segment[1])
|
410
421
|
lexer.tokens.each_with_index do |t, i|
|
411
|
-
type = i.zero? && t.
|
422
|
+
type = i.zero? && t.interpolated_variable? ? :VARIABLE : t.type
|
412
423
|
tokens << new_token(type, t.value, :raw => t.raw)
|
413
424
|
end
|
414
425
|
when :UNENC_VAR
|
@@ -440,7 +451,7 @@ class PuppetLint
|
|
440
451
|
lexer = PuppetLint::Lexer.new
|
441
452
|
lexer.tokenise(segment[1])
|
442
453
|
lexer.tokens.each_with_index do |t, i|
|
443
|
-
type = i.zero? && t.
|
454
|
+
type = i.zero? && t.interpolated_variable? ? :VARIABLE : t.type
|
444
455
|
tokens << new_token(type, t.value, :raw => t.raw)
|
445
456
|
end
|
446
457
|
when :UNENC_VAR
|
@@ -22,6 +22,7 @@ PuppetLint.new_check(:parameter_order) do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
next unless hash_or_array_stack.empty? && paren_stack.empty?
|
25
|
+
next unless parameter?(token)
|
25
26
|
next unless required_parameter?(token)
|
26
27
|
|
27
28
|
prev_tokens = class_idx[:param_tokens][0..i]
|
@@ -38,9 +39,19 @@ PuppetLint.new_check(:parameter_order) do
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
def
|
42
|
+
def parameter?(token)
|
42
43
|
return false unless token.type == :VARIABLE
|
44
|
+
return false unless token.prev_code_token
|
45
|
+
|
46
|
+
[
|
47
|
+
:LPAREN, # First parameter, no type specification
|
48
|
+
:COMMA, # Subsequent parameter, no type specification
|
49
|
+
:TYPE, # Parameter with simple type specification
|
50
|
+
:RBRACK, # Parameter with complex type specification
|
51
|
+
].include?(token.prev_code_token.type)
|
52
|
+
end
|
43
53
|
|
54
|
+
def required_parameter?(token)
|
44
55
|
data_type = token.prev_token_of(:TYPE, :skip_blocks => true)
|
45
56
|
return false if data_type && data_type.value == 'Optional'
|
46
57
|
|
@@ -30,8 +30,12 @@ PuppetLint.new_check(:documentation) do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def find_comment_token(start_token)
|
33
|
+
newlines = 0
|
34
|
+
|
33
35
|
prev_token = start_token.prev_token
|
34
36
|
while !prev_token.nil? && WHITESPACE_TOKENS.include?(prev_token.type)
|
37
|
+
newlines += 1 if prev_token.type == :NEWLINE
|
38
|
+
break if newlines > 1
|
35
39
|
prev_token = prev_token.prev_token
|
36
40
|
end
|
37
41
|
|
@@ -26,7 +26,7 @@ PuppetLint.new_check(:ensure_first_param) do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def fix(problem)
|
29
|
-
first_param_name_token =
|
29
|
+
first_param_name_token = problem[:resource][:param_tokens].first
|
30
30
|
first_param_comma_token = first_param_name_token.next_token_of(:COMMA)
|
31
31
|
ensure_param_name_token = first_param_comma_token.next_token_of(:NAME, :value => 'ensure')
|
32
32
|
|
@@ -4,7 +4,7 @@
|
|
4
4
|
#
|
5
5
|
# https://puppet.com/docs/puppet/latest/style_guide.html#quoting
|
6
6
|
PuppetLint.new_check(:double_quoted_strings) do
|
7
|
-
ESCAPE_CHAR_RE = %r{(\\\$|\\"|\\'|'|\r|\t|\\t|\n|\\n|\\\\)}
|
7
|
+
ESCAPE_CHAR_RE = %r{(\\\$|\\"|\\'|'|\r|\t|\\t|\\s|\n|\\n|\\\\)}
|
8
8
|
|
9
9
|
def check
|
10
10
|
tokens.select { |token|
|
@@ -20,6 +20,7 @@ class PuppetLint
|
|
20
20
|
attr_accessor :ignore_paths
|
21
21
|
attr_accessor :with_filename
|
22
22
|
attr_accessor :disable_checks
|
23
|
+
attr_accessor :only_checks
|
23
24
|
attr_accessor :fail_on_warnings
|
24
25
|
attr_accessor :error_level
|
25
26
|
attr_accessor :log_format
|
@@ -40,6 +41,7 @@ class PuppetLint
|
|
40
41
|
@pattern = DEFAULT_PATTERN
|
41
42
|
@with_filename = true
|
42
43
|
@disable_checks = []
|
44
|
+
@only_checks = []
|
43
45
|
@ignore_paths = []
|
44
46
|
|
45
47
|
define(args, &task_block)
|
@@ -55,6 +57,17 @@ class PuppetLint
|
|
55
57
|
task @name do
|
56
58
|
PuppetLint::OptParser.build
|
57
59
|
|
60
|
+
if Array(@only_checks).any?
|
61
|
+
enable_checks = Array(@only_checks).map(&:to_sym)
|
62
|
+
PuppetLint.configuration.checks.each do |check|
|
63
|
+
if enable_checks.include?(check)
|
64
|
+
PuppetLint.configuration.send("enable_#{check}")
|
65
|
+
else
|
66
|
+
PuppetLint.configuration.send("disable_#{check}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
58
71
|
Array(@disable_checks).each do |check|
|
59
72
|
PuppetLint.configuration.send("disable_#{check}")
|
60
73
|
end
|
@@ -79,6 +92,7 @@ class PuppetLint
|
|
79
92
|
matched_files = matched_files.exclude(*@ignore_paths)
|
80
93
|
|
81
94
|
matched_files.to_a.each do |puppet_file|
|
95
|
+
next unless File.file?(puppet_file)
|
82
96
|
linter.file = puppet_file
|
83
97
|
linter.run
|
84
98
|
linter.print_problems
|
@@ -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
data/lib/puppet-lint.rb
CHANGED
@@ -100,9 +100,9 @@ class PuppetLint
|
|
100
100
|
# Returns a format String to be used with String#%.
|
101
101
|
def log_format
|
102
102
|
if configuration.log_format.nil? || configuration.log_format.empty?
|
103
|
-
## recreate previous old log format as far as thats possible.
|
104
103
|
format = '%{KIND}: %{message} on line %{line}'
|
105
104
|
format.prepend('%{path} - ') if configuration.with_filename
|
105
|
+
format.concat(' (check: %{check})')
|
106
106
|
configuration.log_format = format
|
107
107
|
end
|
108
108
|
|
@@ -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
|
@@ -548,7 +548,7 @@ describe PuppetLint::Bin do
|
|
548
548
|
|
549
549
|
its(:exitstatus) { is_expected.to eq(0) }
|
550
550
|
its(:stdout) do
|
551
|
-
is_expected.to eq('WARNING: variable contains a dash on line 3')
|
551
|
+
is_expected.to eq('WARNING: variable contains a dash on line 3 (check: variable_contains_dash)')
|
552
552
|
end
|
553
553
|
end
|
554
554
|
|
@@ -562,7 +562,7 @@ describe PuppetLint::Bin do
|
|
562
562
|
|
563
563
|
its(:exitstatus) { is_expected.to eq(0) }
|
564
564
|
its(:stdout) do
|
565
|
-
is_expected.to eq('WARNING: variable contains an uppercase letter on line 4')
|
565
|
+
is_expected.to eq('WARNING: variable contains an uppercase letter on line 4 (check: variable_is_lowercase)')
|
566
566
|
end
|
567
567
|
end
|
568
568
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe PuppetLint::Lexer::StringSlurper do
|
@@ -36,14 +38,22 @@ describe PuppetLint::Lexer::StringSlurper do
|
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
context 'an escaped $' do
|
41
|
+
context 'an escaped $var' do
|
40
42
|
let(:string) { '\$foo"' }
|
41
43
|
|
42
|
-
it 'does not create an
|
44
|
+
it 'does not create an unenclosed variable segment' do
|
43
45
|
expect(segments).to eq([[:STRING, '\$foo']])
|
44
46
|
end
|
45
47
|
end
|
46
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
|
+
|
47
57
|
context 'a variable and a suffix' do
|
48
58
|
let(:string) { '${foo}bar"' }
|
49
59
|
|
@@ -238,6 +248,30 @@ describe PuppetLint::Lexer::StringSlurper do
|
|
238
248
|
end
|
239
249
|
end
|
240
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
|
+
|
241
275
|
context 'an interpolation with a complex function chain' do
|
242
276
|
let(:string) { '${key} ${flatten([$value]).join("\nkey ")}"' }
|
243
277
|
|
@@ -284,6 +318,18 @@ describe PuppetLint::Lexer::StringSlurper do
|
|
284
318
|
describe '#parse_heredoc' do
|
285
319
|
subject(:segments) { described_class.new(heredoc).parse_heredoc(heredoc_tag) }
|
286
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
|
+
|
287
333
|
context 'when parsing a heredoc with interpolation disabled' do
|
288
334
|
context 'that is a plain heredoc' do
|
289
335
|
let(:heredoc) { %( SOMETHING\n ELSE\n :\n |-myheredoc) }
|
@@ -404,4 +450,24 @@ describe PuppetLint::Lexer::StringSlurper do
|
|
404
450
|
end
|
405
451
|
end
|
406
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
|
407
473
|
end
|