puppet-lint 0.2.0.pre1 → 0.2.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.
Files changed (42) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +1 -14
  3. data/Gemfile +2 -0
  4. data/Rakefile +5 -0
  5. data/bin/puppet-lint +1 -99
  6. data/lib/puppet-lint.rb +4 -12
  7. data/lib/puppet-lint/bin.rb +115 -0
  8. data/lib/puppet-lint/configuration.rb +6 -2
  9. data/lib/puppet-lint/lexer.rb +135 -83
  10. data/lib/puppet-lint/lexer/token.rb +62 -0
  11. data/lib/puppet-lint/plugin.rb +57 -51
  12. data/lib/puppet-lint/plugins.rb +2 -0
  13. data/lib/puppet-lint/plugins/check_classes.rb +161 -45
  14. data/lib/puppet-lint/plugins/check_comments.rb +33 -0
  15. data/lib/puppet-lint/plugins/check_conditionals.rb +8 -10
  16. data/lib/puppet-lint/plugins/check_documentation.rb +41 -0
  17. data/lib/puppet-lint/plugins/check_resources.rb +28 -2
  18. data/lib/puppet-lint/plugins/check_strings.rb +6 -4
  19. data/lib/puppet-lint/plugins/check_variables.rb +1 -1
  20. data/lib/puppet-lint/plugins/check_whitespace.rb +26 -49
  21. data/lib/puppet-lint/tasks/puppet-lint.rb +2 -1
  22. data/lib/puppet-lint/version.rb +1 -1
  23. data/puppet-lint.gemspec +1 -0
  24. data/spec/fixtures/test/manifests/fail.pp +2 -0
  25. data/spec/fixtures/test/manifests/init.pp +3 -0
  26. data/spec/fixtures/test/manifests/warning.pp +2 -0
  27. data/spec/puppet-lint/bin_spec.rb +266 -0
  28. data/spec/puppet-lint/configuration_spec.rb +51 -0
  29. data/spec/puppet-lint/lexer/token_spec.rb +18 -0
  30. data/spec/puppet-lint/lexer_spec.rb +738 -0
  31. data/spec/puppet-lint/{check_classes_spec.rb → plugins/check_classes_spec.rb} +74 -7
  32. data/spec/puppet-lint/plugins/check_comments_spec.rb +40 -0
  33. data/spec/puppet-lint/{check_conditionals_spec.rb → plugins/check_conditionals_spec.rb} +19 -0
  34. data/spec/puppet-lint/plugins/check_documentation_spec.rb +55 -0
  35. data/spec/puppet-lint/{check_resources_spec.rb → plugins/check_resources_spec.rb} +65 -0
  36. data/spec/puppet-lint/{check_strings_spec.rb → plugins/check_strings_spec.rb} +18 -1
  37. data/spec/puppet-lint/{check_variables_spec.rb → plugins/check_variables_spec.rb} +0 -0
  38. data/spec/puppet-lint/plugins/check_whitespace_spec.rb +291 -0
  39. data/spec/puppet-lint_spec.rb +10 -0
  40. data/spec/spec_helper.rb +5 -0
  41. metadata +58 -24
  42. data/spec/puppet-lint/check_whitespace_spec.rb +0 -120
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe PuppetLint::Configuration do
4
+ subject { PuppetLint::Configuration.new }
5
+
6
+ it 'should create check methods on the fly' do
7
+ subject.add_check('foo')
8
+
9
+ subject.should respond_to(:foo_enabled?)
10
+ subject.should_not respond_to(:bar_enabled?)
11
+ subject.should respond_to(:enable_foo)
12
+ subject.should respond_to(:disable_foo)
13
+
14
+ subject.disable_foo
15
+ subject.settings['foo_disabled'].should == true
16
+ subject.foo_enabled?.should == false
17
+
18
+ subject.enable_foo
19
+ subject.settings['foo_disabled'].should == false
20
+ subject.foo_enabled?.should == true
21
+ end
22
+
23
+ it 'should know what checks have been added' do
24
+ subject.checks.should include('foo')
25
+ end
26
+
27
+ it 'should respond nil to unknown config options' do
28
+ subject.foobarbaz.should == nil
29
+ end
30
+
31
+ it 'should create options on the fly' do
32
+ subject.add_option('bar')
33
+
34
+ subject.bar.should == nil
35
+
36
+ subject.bar = 'aoeui'
37
+ subject.bar.should == 'aoeui'
38
+ end
39
+
40
+ it 'should be able to set sane defaults' do
41
+ subject.defaults
42
+
43
+ subject.settings.should == {
44
+ 'with_filename' => false,
45
+ 'fail_on_warnings' => false,
46
+ 'error_level' => :all,
47
+ 'log_format' => '',
48
+ }
49
+ end
50
+ end
51
+
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe PuppetLint::Lexer::Token do
4
+ subject do
5
+ PuppetLint::Lexer::Token.new(:NAME, 'foo', 1, 2)
6
+ end
7
+
8
+ it { should respond_to(:type) }
9
+ it { should respond_to(:value) }
10
+ it { should respond_to(:line) }
11
+ it { should respond_to(:column) }
12
+
13
+ its(:type) { should == :NAME }
14
+ its(:value) { should == 'foo' }
15
+ its(:line) { should == 1 }
16
+ its(:column) { should == 2 }
17
+ its(:inspect) { should == "<Token :NAME (foo) @1:2>" }
18
+ end
@@ -0,0 +1,738 @@
1
+ require 'spec_helper'
2
+
3
+ describe PuppetLint::Lexer do
4
+ before do
5
+ @lexer = PuppetLint::Lexer.new
6
+ end
7
+
8
+ context 'invalid code' do
9
+ it 'should bork' do
10
+ expect { @lexer.tokenise('%') }.to raise_error(PuppetLint::LexerError)
11
+ end
12
+ end
13
+
14
+ context '#new_token' do
15
+ it 'should calculate the line number for an empty string' do
16
+ token = @lexer.new_token(:TEST, 'test', :chunk => '')
17
+ token.line.should == 1
18
+ end
19
+
20
+ it 'should calculate the line number for a multi line string' do
21
+ token = @lexer.new_token(:TEST, 'test', :chunk => "foo\nbar")
22
+ token.line.should == 2
23
+ end
24
+
25
+ it 'should calculate the column number for an empty string' do
26
+ token = @lexer.new_token(:TEST, 'test', :chunk => '')
27
+ token.column.should == 1
28
+ end
29
+
30
+ it 'should calculate the column number for a single line string' do
31
+ token = @lexer.new_token(:TEST, 'test', :chunk => 'this is a test')
32
+ token.column.should == 14
33
+ end
34
+
35
+ it 'should calculate the column number for a multi line string' do
36
+ token = @lexer.new_token(:TEST, 'test', :chunk => "foo\nbar\nbaz\ngronk")
37
+ token.column.should == 5
38
+ end
39
+ end
40
+
41
+ context '#get_string_segment' do
42
+ it 'should get a segment with a single terminator' do
43
+ data = StringScanner.new('foo"bar')
44
+ value, terminator = @lexer.get_string_segment(data, '"')
45
+ value.should == 'foo'
46
+ terminator.should == '"'
47
+ end
48
+
49
+ it 'should get a segment with multiple terminators' do
50
+ data = StringScanner.new('foo"bar$baz')
51
+ value, terminator = @lexer.get_string_segment(data, "'$")
52
+ value.should == 'foo"bar'
53
+ terminator.should == '$'
54
+ end
55
+
56
+ it 'should not get a segment with an escaped terminator' do
57
+ data = StringScanner.new('foo"bar')
58
+ value, terminator = @lexer.get_string_segment(data, '$')
59
+ value.should be_nil
60
+ terminator.should be_nil
61
+ end
62
+ end
63
+
64
+ context '#interpolate_string' do
65
+ it 'should handle a string with no variables' do
66
+ @lexer.interpolate_string('foo bar baz"',1, 1)
67
+ token = @lexer.tokens.first
68
+
69
+ @lexer.tokens.length.should == 1
70
+ token.type.should == :STRING
71
+ token.value.should == 'foo bar baz'
72
+ token.line.should == 1
73
+ token.column.should == 1
74
+ end
75
+
76
+ it 'should handle a string with a newline' do
77
+ @lexer.interpolate_string(%{foo\nbar"}, 1, 1)
78
+ token = @lexer.tokens.first
79
+
80
+ @lexer.tokens.length.should == 1
81
+ token.type.should == :STRING
82
+ token.value.should == "foo\nbar"
83
+ token.line.should == 1
84
+ token.column.should == 1
85
+ end
86
+
87
+ it 'should handle a string with a single variable and suffix' do
88
+ @lexer.interpolate_string('${foo}bar"', 1, 1)
89
+ tokens = @lexer.tokens
90
+
91
+ tokens.length.should == 3
92
+
93
+ tokens[0].type.should == :DQPRE
94
+ tokens[0].value.should == ''
95
+ tokens[0].line.should == 1
96
+ tokens[0].column.should == 1
97
+
98
+ tokens[1].type.should == :VARIABLE
99
+ tokens[1].value.should == 'foo'
100
+ tokens[1].line.should == 1
101
+ tokens[1].column.should == 3
102
+
103
+ tokens[2].type.should == :DQPOST
104
+ tokens[2].value.should == 'bar'
105
+ tokens[2].line.should == 1
106
+ tokens[2].column.should == 8
107
+ end
108
+
109
+ it 'should handle a string with a single variable and surrounding text' do
110
+ @lexer.interpolate_string('foo${bar}baz"', 1, 1)
111
+ tokens = @lexer.tokens
112
+
113
+ tokens.length.should == 3
114
+
115
+ tokens[0].type.should == :DQPRE
116
+ tokens[0].value.should == 'foo'
117
+ tokens[0].line.should == 1
118
+ tokens[0].column.should == 1
119
+
120
+ tokens[1].type.should == :VARIABLE
121
+ tokens[1].value.should == 'bar'
122
+ tokens[1].line.should == 1
123
+ tokens[1].column.should == 6
124
+
125
+ tokens[2].type.should == :DQPOST
126
+ tokens[2].value.should == 'baz'
127
+ tokens[2].line.should == 1
128
+ tokens[2].column.should == 11
129
+ end
130
+
131
+ it 'should handle a string with multiple variables and surrounding text' do
132
+ @lexer.interpolate_string('foo${bar}baz${gronk}meh"', 1, 1)
133
+ tokens = @lexer.tokens
134
+
135
+ tokens.length.should == 5
136
+
137
+ tokens[0].type.should == :DQPRE
138
+ tokens[0].value.should == 'foo'
139
+ tokens[0].line.should == 1
140
+ tokens[0].column.should == 1
141
+
142
+ tokens[1].type.should == :VARIABLE
143
+ tokens[1].value.should == 'bar'
144
+ tokens[1].line.should == 1
145
+ tokens[1].column.should == 6
146
+
147
+ tokens[2].type.should == :DQMID
148
+ tokens[2].value.should == 'baz'
149
+ tokens[2].line.should == 1
150
+ tokens[2].column.should == 11
151
+
152
+ tokens[3].type.should == :VARIABLE
153
+ tokens[3].value.should == 'gronk'
154
+ tokens[3].line.should == 1
155
+ tokens[3].column.should == 15
156
+
157
+ tokens[4].type.should == :DQPOST
158
+ tokens[4].value.should == 'meh'
159
+ tokens[4].line.should == 1
160
+ tokens[4].column.should == 22
161
+ end
162
+
163
+ it 'should handle a string with only a single variable' do
164
+ @lexer.interpolate_string('${bar}"', 1, 1)
165
+ tokens = @lexer.tokens
166
+
167
+ tokens.length.should == 3
168
+
169
+ tokens[0].type.should == :DQPRE
170
+ tokens[0].value.should == ''
171
+ tokens[0].line.should == 1
172
+ tokens[0].column.should == 1
173
+
174
+ tokens[1].type.should == :VARIABLE
175
+ tokens[1].value.should == 'bar'
176
+ tokens[1].line.should == 1
177
+ tokens[1].column.should == 3
178
+
179
+ tokens[2].type.should == :DQPOST
180
+ tokens[2].value.should == ''
181
+ tokens[2].line.should == 1
182
+ tokens[2].column.should == 8
183
+ end
184
+
185
+ it 'should handle a string with only many variables' do
186
+ @lexer.interpolate_string('${bar}${gronk}"', 1, 1)
187
+ tokens = @lexer.tokens
188
+
189
+ tokens.length.should == 5
190
+
191
+ tokens[0].type.should == :DQPRE
192
+ tokens[0].value.should == ''
193
+ tokens[0].line.should == 1
194
+ tokens[0].column.should == 1
195
+
196
+ tokens[1].type.should == :VARIABLE
197
+ tokens[1].value.should == 'bar'
198
+ tokens[1].line.should == 1
199
+ tokens[1].column.should == 3
200
+
201
+ tokens[2].type.should == :DQMID
202
+ tokens[2].value.should == ''
203
+ tokens[2].line.should == 1
204
+ tokens[2].column.should == 8
205
+
206
+ tokens[3].type.should == :VARIABLE
207
+ tokens[3].value.should == 'gronk'
208
+ tokens[3].line.should == 1
209
+ tokens[3].column.should == 9
210
+
211
+ tokens[4].type.should == :DQPOST
212
+ tokens[4].value.should == ''
213
+ tokens[4].line.should == 1
214
+ tokens[4].column.should == 16
215
+ end
216
+
217
+ it 'should handle a string with only an unenclosed variable' do
218
+ @lexer.interpolate_string('$foo"', 1, 1)
219
+ tokens = @lexer.tokens
220
+
221
+ tokens.length.should == 3
222
+
223
+ tokens[0].type.should == :DQPRE
224
+ tokens[0].value.should == ''
225
+ tokens[0].line.should == 1
226
+ tokens[0].column.should == 1
227
+
228
+ tokens[1].type.should == :UNENC_VARIABLE
229
+ tokens[1].value.should == 'foo'
230
+ tokens[1].line.should == 1
231
+ tokens[1].column.should == 2
232
+
233
+ tokens[2].type.should == :DQPOST
234
+ tokens[2].value.should == ''
235
+ tokens[2].line.should == 1
236
+ tokens[2].column.should == 6
237
+ end
238
+
239
+ it 'should handle a string with a nested string inside it' do
240
+ @lexer.interpolate_string(%q{string with ${'a nested single quoted string'} inside it"}, 1, 1)
241
+ tokens = @lexer.tokens
242
+
243
+ tokens.length.should == 3
244
+
245
+ tokens[0].type.should == :DQPRE
246
+ tokens[0].value.should == 'string with '
247
+ tokens[0].line.should == 1
248
+ tokens[0].column.should == 1
249
+
250
+ tokens[1].type.should == :SSTRING
251
+ tokens[1].value.should == 'a nested single quoted string'
252
+ tokens[1].line.should == 1
253
+ tokens[1].column.should == 16
254
+
255
+ tokens[2].type.should == :DQPOST
256
+ tokens[2].value.should == ' inside it'
257
+ tokens[2].line.should == 1
258
+ tokens[2].column.should == 48
259
+ end
260
+
261
+ it 'should handle a string with nested math' do
262
+ @lexer.interpolate_string(%q{string with ${(3+5)/4} nested math"}, 1, 1)
263
+ tokens = @lexer.tokens
264
+
265
+ tokens.length.should == 9
266
+
267
+ tokens[0].type.should == :DQPRE
268
+ tokens[0].value.should == 'string with '
269
+ tokens[0].line.should == 1
270
+ tokens[0].column.should == 1
271
+
272
+ tokens[1].type.should == :LPAREN
273
+ tokens[1].line.should == 1
274
+ tokens[1].column.should == 16
275
+
276
+ tokens[2].type.should == :NUMBER
277
+ tokens[2].value.should == '3'
278
+ tokens[2].line.should == 1
279
+ tokens[2].column.should == 17
280
+
281
+ tokens[3].type.should == :PLUS
282
+ tokens[3].line.should == 1
283
+ tokens[3].column.should == 18
284
+
285
+ tokens[4].type.should == :NUMBER
286
+ tokens[4].value.should == '5'
287
+ tokens[4].line.should == 1
288
+ tokens[4].column.should == 19
289
+
290
+ tokens[5].type.should == :RPAREN
291
+ tokens[5].line.should == 1
292
+ tokens[5].column.should == 20
293
+
294
+ tokens[6].type.should == :DIV
295
+ tokens[6].line.should == 1
296
+ tokens[6].column.should == 21
297
+
298
+ tokens[7].type.should == :NUMBER
299
+ tokens[7].value.should == '4'
300
+ tokens[7].line.should == 1
301
+ tokens[7].column.should == 22
302
+
303
+ tokens[8].type.should == :DQPOST
304
+ tokens[8].value.should == ' nested math'
305
+ tokens[8].line.should == 1
306
+ tokens[8].column.should == 24
307
+ end
308
+
309
+ it 'should handle a string with a nested array' do
310
+ @lexer.interpolate_string(%q{string with ${['an array ', $v2]} in it"}, 1, 1)
311
+ tokens = @lexer.tokens
312
+
313
+ tokens.length.should == 8
314
+
315
+ tokens[0].type.should == :DQPRE
316
+ tokens[0].value.should == 'string with '
317
+ tokens[0].line.should == 1
318
+ tokens[0].column.should == 1
319
+
320
+ tokens[1].type.should == :LBRACK
321
+ tokens[1].line.should == 1
322
+ tokens[1].column.should == 16
323
+
324
+ tokens[2].type.should == :SSTRING
325
+ tokens[2].value.should == 'an array '
326
+ tokens[2].line.should == 1
327
+ tokens[2].column.should == 17
328
+
329
+ tokens[3].type.should == :COMMA
330
+ tokens[3].line.should == 1
331
+ tokens[3].column.should == 28
332
+
333
+ tokens[4].type.should == :WHITESPACE
334
+ tokens[4].value.should == ' '
335
+ tokens[4].line.should == 1
336
+ tokens[4].column.should == 29
337
+
338
+ tokens[5].type.should == :VARIABLE
339
+ tokens[5].value.should == 'v2'
340
+ tokens[5].line.should == 1
341
+ tokens[5].column.should == 30
342
+
343
+ tokens[6].type.should == :RBRACK
344
+ tokens[6].line.should == 1
345
+ tokens[6].column.should == 33
346
+
347
+ tokens[7].type.should == :DQPOST
348
+ tokens[7].value.should == ' in it'
349
+ tokens[7].line.should == 1
350
+ tokens[7].column.should == 35
351
+ end
352
+
353
+ it 'should handle a string of $s' do
354
+ @lexer.interpolate_string(%q{$$$$"}, 1, 1)
355
+ tokens = @lexer.tokens
356
+
357
+ tokens.length.should == 1
358
+
359
+ tokens[0].type.should == :STRING
360
+ tokens[0].value.should == '$$$$'
361
+ tokens[0].line.should == 1
362
+ tokens[0].column.should == 1
363
+ end
364
+
365
+ it 'should handle "$foo$bar"' do
366
+ @lexer.interpolate_string(%q{$foo$bar"}, 1, 1)
367
+ tokens = @lexer.tokens
368
+
369
+ tokens.length.should == 5
370
+
371
+ tokens[0].type.should == :DQPRE
372
+ tokens[0].value.should == ''
373
+ tokens[0].line.should == 1
374
+ tokens[0].column.should == 1
375
+
376
+ tokens[1].type.should == :UNENC_VARIABLE
377
+ tokens[1].value.should == 'foo'
378
+ tokens[1].line.should == 1
379
+ tokens[1].column.should == 2
380
+
381
+ tokens[2].type.should == :DQMID
382
+ tokens[2].value.should == ''
383
+ tokens[2].line.should == 1
384
+ tokens[2].column.should == 6
385
+
386
+ tokens[3].type.should == :UNENC_VARIABLE
387
+ tokens[3].value.should == 'bar'
388
+ tokens[3].line.should == 1
389
+ tokens[3].column.should == 6
390
+
391
+ tokens[4].type.should == :DQPOST
392
+ tokens[4].value.should == ''
393
+ tokens[4].line.should == 1
394
+ tokens[4].column.should == 10
395
+ end
396
+
397
+ it 'should handle "foo$bar$"' do
398
+ @lexer.interpolate_string(%q{foo$bar$"}, 1, 1)
399
+ tokens = @lexer.tokens
400
+
401
+ tokens.length.should == 3
402
+
403
+ tokens[0].type.should == :DQPRE
404
+ tokens[0].value.should == 'foo'
405
+ tokens[0].line.should == 1
406
+ tokens[0].column.should == 1
407
+
408
+ tokens[1].type.should == :UNENC_VARIABLE
409
+ tokens[1].value.should == 'bar'
410
+ tokens[1].line.should == 1
411
+ tokens[1].column.should == 5
412
+
413
+ tokens[2].type.should == :DQPOST
414
+ tokens[2].value.should == '$'
415
+ tokens[2].line.should == 1
416
+ tokens[2].column.should == 9
417
+ end
418
+
419
+ it 'should handle "foo$$bar"' do
420
+ @lexer.interpolate_string(%q{foo$$bar"}, 1, 1)
421
+ tokens = @lexer.tokens
422
+
423
+ tokens.length.should == 3
424
+
425
+ tokens[0].type.should == :DQPRE
426
+ tokens[0].value.should == 'foo$'
427
+ tokens[0].line.should == 1
428
+ tokens[0].column.should == 1
429
+
430
+ tokens[1].type.should == :UNENC_VARIABLE
431
+ tokens[1].value.should == 'bar'
432
+ tokens[1].line.should == 1
433
+ tokens[1].column.should == 6
434
+
435
+ tokens[2].type.should == :DQPOST
436
+ tokens[2].value.should == ''
437
+ tokens[2].line.should == 1
438
+ tokens[2].column.should == 10
439
+ end
440
+
441
+ it 'should handle an empty string' do
442
+ @lexer.interpolate_string(%q{"}, 1, 1)
443
+ tokens = @lexer.tokens
444
+
445
+ tokens.length.should == 1
446
+
447
+ tokens[0].type.should == :STRING
448
+ tokens[0].value.should == ''
449
+ tokens[0].line.should == 1
450
+ tokens[0].column.should == 1
451
+ end
452
+
453
+ it 'should handle "$foo::::bar"' do
454
+ @lexer.interpolate_string(%q{$foo::::bar"}, 1, 1)
455
+ tokens = @lexer.tokens
456
+
457
+ tokens.length.should == 3
458
+
459
+ tokens[0].type.should == :DQPRE
460
+ tokens[0].value.should == ''
461
+ tokens[0].line.should == 1
462
+ tokens[0].column.should == 1
463
+
464
+ tokens[1].type.should == :UNENC_VARIABLE
465
+ tokens[1].value.should == 'foo'
466
+ tokens[1].line.should == 1
467
+ tokens[1].column.should == 2
468
+
469
+ tokens[2].type.should == :DQPOST
470
+ tokens[2].value.should == '::::bar'
471
+ tokens[2].line.should == 1
472
+ tokens[2].column.should == 6
473
+ end
474
+ end
475
+
476
+ [
477
+ 'case',
478
+ 'class',
479
+ 'default',
480
+ 'define',
481
+ 'import',
482
+ 'if',
483
+ 'elsif',
484
+ 'else',
485
+ 'inherits',
486
+ 'node',
487
+ 'and',
488
+ 'or',
489
+ 'undef',
490
+ 'true',
491
+ 'false',
492
+ 'in',
493
+ 'unless',
494
+ ].each do |keyword|
495
+ it "should handle '#{keyword}' as a keyword" do
496
+ token = @lexer.tokenise(keyword).first
497
+ token.type.should == keyword.upcase.to_sym
498
+ token.value.should == keyword
499
+ end
500
+ end
501
+
502
+ [
503
+ [:LBRACK, '['],
504
+ [:RBRACK, ']'],
505
+ [:LBRACE, '{'],
506
+ [:RBRACE, '}'],
507
+ [:LPAREN, '('],
508
+ [:RPAREN, ')'],
509
+ [:EQUALS, '='],
510
+ [:ISEQUAL, '=='],
511
+ [:GREATEREQUAL, '>='],
512
+ [:GREATERTHAN, '>'],
513
+ [:LESSTHAN, '<'],
514
+ [:LESSEQUAL, '<='],
515
+ [:NOTEQUAL, '!='],
516
+ [:NOT, '!'],
517
+ [:COMMA, ','],
518
+ [:DOT, '.'],
519
+ [:COLON, ':'],
520
+ [:AT, '@'],
521
+ [:LLCOLLECT, '<<|'],
522
+ [:RRCOLLECT, '|>>'],
523
+ [:LCOLLECT, '<|'],
524
+ [:RCOLLECT, '|>'],
525
+ [:SEMIC, ';'],
526
+ [:QMARK, '?'],
527
+ [:BACKSLASH, '\\'],
528
+ [:FARROW, '=>'],
529
+ [:PARROW, '+>'],
530
+ [:APPENDS, '+='],
531
+ [:PLUS, '+'],
532
+ [:MINUS, '-'],
533
+ [:DIV, '/'],
534
+ [:TIMES, '*'],
535
+ [:LSHIFT, '<<'],
536
+ [:RSHIFT, '>>'],
537
+ [:MATCH, '=~'],
538
+ [:NOMATCH, '!~'],
539
+ [:IN_EDGE, '->'],
540
+ [:OUT_EDGE, '<-'],
541
+ [:IN_EDGE_SUB, '~>'],
542
+ [:OUT_EDGE_SUB, '<~'],
543
+ ].each do |name, string|
544
+ it "should have a token named '#{name.to_s}'" do
545
+ token = @lexer.tokenise(string).first
546
+ token.type.should == name
547
+ token.value.should == string
548
+ end
549
+ end
550
+
551
+ context ':CLASSREF' do
552
+ it 'should match single capitalised alphanumeric term' do
553
+ token = @lexer.tokenise('One').first
554
+ token.type.should == :CLASSREF
555
+ token.value.should == 'One'
556
+ end
557
+
558
+ it 'should match two capitalised alphanumeric terms sep by ::' do
559
+ token = @lexer.tokenise('One::Two').first
560
+ token.type.should == :CLASSREF
561
+ token.value.should == 'One::Two'
562
+ end
563
+
564
+ it 'should match many capitalised alphanumeric terms sep by ::' do
565
+ token = @lexer.tokenise('One::Two::Three::Four::Five').first
566
+ token.type.should == :CLASSREF
567
+ token.value.should == 'One::Two::Three::Four::Five'
568
+ end
569
+
570
+ it 'should match capitalised terms prefixed by ::' do
571
+ token = @lexer.tokenise('::One').first
572
+ token.type.should == :CLASSREF
573
+ token.value.should == '::One'
574
+ end
575
+ end
576
+
577
+ context ':NAME' do
578
+ it 'should match lowercase alphanumeric terms' do
579
+ token = @lexer.tokenise('one-two').first
580
+ token.type.should == :NAME
581
+ token.value.should == 'one-two'
582
+ end
583
+
584
+ it 'should match lowercase alphanumeric terms sep by ::' do
585
+ token = @lexer.tokenise('one::two').first
586
+ token.type.should == :NAME
587
+ token.value.should == 'one::two'
588
+ end
589
+
590
+ it 'should match many lowercase alphanumeric terms sep by ::' do
591
+ token = @lexer.tokenise('one::two::three::four::five').first
592
+ token.type.should == :NAME
593
+ token.value.should == 'one::two::three::four::five'
594
+ end
595
+
596
+ it 'should match lowercase alphanumeric terms prefixed by ::' do
597
+ token = @lexer.tokenise('::1one::2two::3three').first
598
+ token.type.should == :NAME
599
+ token.value.should == '::1one::2two::3three'
600
+ end
601
+ end
602
+
603
+ context ':NUMBER' do
604
+ it 'should match numeric terms' do
605
+ token = @lexer.tokenise('1234567890').first
606
+ token.type.should == :NUMBER
607
+ token.value.should == '1234567890'
608
+ end
609
+
610
+ it 'should match float terms' do
611
+ token = @lexer.tokenise('12345.6789').first
612
+ token.type.should == :NUMBER
613
+ token.value.should == '12345.6789'
614
+ end
615
+
616
+ it 'should match hexadecimal terms' do
617
+ token = @lexer.tokenise('0xCAFE1029').first
618
+ token.type.should == :NUMBER
619
+ token.value.should == '0xCAFE1029'
620
+ end
621
+
622
+ it 'should match float with exponent terms' do
623
+ token = @lexer.tokenise('10e23').first
624
+ token.type.should == :NUMBER
625
+ token.value.should == '10e23'
626
+ end
627
+
628
+ it 'should match float with negative exponent terms' do
629
+ token = @lexer.tokenise('10e-23').first
630
+ token.type.should == :NUMBER
631
+ token.value.should == '10e-23'
632
+ end
633
+
634
+ it 'should match float with exponent terms' do
635
+ token = @lexer.tokenise('1.234e5').first
636
+ token.type.should == :NUMBER
637
+ token.value.should == '1.234e5'
638
+ end
639
+ end
640
+
641
+ context ':COMMENT' do
642
+ it 'should match everything on a line after #' do
643
+ token = @lexer.tokenise('foo # bar baz')[2]
644
+ token.type.should == :COMMENT
645
+ token.value.should == 'bar baz'
646
+ end
647
+ end
648
+
649
+ context ':MLCOMMENT' do
650
+ it 'should match comments on a single line' do
651
+ token = @lexer.tokenise('/* foo bar */').first
652
+ token.type.should == :MLCOMMENT
653
+ token.value.should == 'foo bar'
654
+ end
655
+
656
+ it 'should match comments on multiple lines' do
657
+ token = @lexer.tokenise("/*\n * foo bar\n*/").first
658
+ token.type.should == :MLCOMMENT
659
+ token.value.should == 'foo bar'
660
+ end
661
+ end
662
+
663
+ context ':SLASH_COMMENT' do
664
+ it 'should match everyone on a line after //' do
665
+ token = @lexer.tokenise('foo // bar baz')[2]
666
+ token.type.should == :SLASH_COMMENT
667
+ token.value.should == 'bar baz'
668
+ end
669
+ end
670
+
671
+ context ':SSTRING' do
672
+ it 'should match a single quoted string' do
673
+ token = @lexer.tokenise("'single quoted string'").first
674
+ token.type.should == :SSTRING
675
+ token.value.should == 'single quoted string'
676
+ end
677
+
678
+ it "should match a single quoted string with an escaped '" do
679
+ token = @lexer.tokenise(%q{'single quoted string with "\\'"'}).first
680
+ token.type.should == :SSTRING
681
+ token.value.should == 'single quoted string with "\\\'"'
682
+ end
683
+
684
+ it "should match a single quoted string with an escaped $" do
685
+ token = @lexer.tokenise(%q{'single quoted string with "\$"'}).first
686
+ token.type.should == :SSTRING
687
+ token.value.should == 'single quoted string with "\\$"'
688
+ end
689
+
690
+ it "should match a single quoted string with an escaped ." do
691
+ token = @lexer.tokenise(%q{'single quoted string with "\."'}).first
692
+ token.type.should == :SSTRING
693
+ token.value.should == 'single quoted string with "\\."'
694
+ end
695
+
696
+ it "should match a single quoted string with an escaped \\n" do
697
+ token = @lexer.tokenise(%q{'single quoted string with "\n"'}).first
698
+ token.type.should == :SSTRING
699
+ token.value.should == 'single quoted string with "\\n"'
700
+ end
701
+
702
+ it "should match a single quoted string with an escaped \\" do
703
+ token = @lexer.tokenise(%q{'single quoted string with "\\\\"'}).first
704
+ token.type.should == :SSTRING
705
+ token.value.should == 'single quoted string with "\\\\"'
706
+ end
707
+
708
+ it "should match an empty string" do
709
+ token = @lexer.tokenise("''").first
710
+ token.type.should == :SSTRING
711
+ token.value.should == ''
712
+ end
713
+ end
714
+
715
+ context ':REGEX' do
716
+ it 'should match anything enclosed in //' do
717
+ token = @lexer.tokenise('/this is a regex/').first
718
+ token.type.should == :REGEX
719
+ token.value.should == 'this is a regex'
720
+ end
721
+
722
+ it 'should not match if there is \n in the regex' do
723
+ token = @lexer.tokenise("/this is \n a regex/").first
724
+ token.type.should_not == :REGEX
725
+ end
726
+
727
+ it 'should not consider \/ to be the end of the regex' do
728
+ token = @lexer.tokenise('/this is \/ a regex/').first
729
+ token.type.should == :REGEX
730
+ token.value.should == 'this is \\/ a regex'
731
+ end
732
+
733
+ it 'should not match chained division' do
734
+ tokens = @lexer.tokenise('$x = $a/$b/$c')
735
+ tokens.select { |r| r.type == :REGEX }.should == []
736
+ end
737
+ end
738
+ end