puppet-lint 2.4.0 → 2.5.2
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 +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
|