1#!perl -w
2
3use strict;
4use warnings;
5use lib 't/lib';
6
7use Test::More;
8
9use TAP::Parser::YAMLish::Reader;
10
11my @SCHEDULE;
12
13BEGIN {
14    @SCHEDULE = (
15        {   name => 'Hello World',
16            in   => [
17                '--- Hello, World',
18                '...',
19            ],
20            out => "Hello, World",
21        },
22        {   name => 'Hello World 2',
23            in   => [
24                '--- \'Hello, \'\'World\'',
25                '...',
26            ],
27            out => "Hello, 'World",
28        },
29        {   name => 'Hello World 3',
30            in   => [
31                '--- "Hello, World"',
32                '...',
33            ],
34            out => "Hello, World",
35        },
36        {   name => 'Hello World 4',
37            in   => [
38                '--- "Hello, World"',
39                '...',
40            ],
41            out => "Hello, World",
42        },
43        {   name => 'Hello World 4',
44            in   => [
45                '--- >',
46                '   Hello,',
47                '      World',
48                '...',
49            ],
50            out => "Hello, World\n",
51        },
52        {   name => 'Hello World Block',
53            in   => [
54                '--- |',
55                '   Hello,',
56                '      World',
57                '...',
58            ],
59            out => "Hello,\n   World\n",
60        },
61        {   name => 'Hello World 5',
62            in   => [
63                '--- >',
64                '   Hello,',
65                '  World',
66                '...',
67            ],
68            error => qr{Missing\s+'[.][.][.]'},
69        },
70        {   name => 'Simple array',
71            in   => [
72                '---',
73                '- 1',
74                '- 2',
75                '- 3',
76                '...',
77            ],
78            out => [ '1', '2', '3' ],
79        },
80        {   name => 'Mixed array',
81            in   => [
82                '---',
83                '- 1',
84                '- \'two\'',
85                '- "three\n"',
86                '...',
87            ],
88            out => [ '1', 'two', "three\n" ],
89        },
90        {   name => 'Hash in array',
91            in   => [
92                '---',
93                '- 1',
94                '- two: 2',
95                '- 3',
96                '...',
97            ],
98            out => [ '1', { two => '2' }, '3' ],
99        },
100        {   name => 'Hash in array 2',
101            in   => [
102                '---',
103                '- 1',
104                '- two: 2',
105                '  three: 3',
106                '- 4',
107                '...',
108            ],
109            out => [ '1', { two => '2', three => '3' }, '4' ],
110        },
111        {   name => 'Nested array',
112            in   => [
113                '---',
114                '- one',
115                '-',
116                '  - two',
117                '  -',
118                '    - three',
119                '  - four',
120                '- five',
121                '...',
122            ],
123            out => [ 'one', [ 'two', ['three'], 'four' ], 'five' ],
124        },
125        {   name => 'Nested hash',
126            in   => [
127                '---',
128                'one:',
129                '  five: 5',
130                '  two:',
131                '    four: 4',
132                '    three: 3',
133                'six: 6',
134                '...',
135            ],
136            out => {
137                one => { two => { three => '3', four => '4' }, five => '5' },
138                six => '6'
139            },
140        },
141        {   name => 'Edge cases for hash start vs. undefined scalar 1',
142            in   => [
143                '---',
144                'one:',
145                '  five: 5',
146                '  two:',
147                '  four: 4',
148                '  three: 3',
149                'six: 6',
150                '...',
151            ],
152            out => {
153                one => { two => undef, three => '3', four => '4', five => '5' },
154                six => '6'
155            },
156        },
157        {   name => 'Edge cases for hash start vs. undefined scalar 2',
158            in   => [
159                '---',
160                'one:',
161                '  five: 5',
162                '  two: ~',
163                '  four: 4',
164                '  three: 3',
165                'six: 6',
166                '...',
167            ],
168            out => {
169                one => { two => undef, three => '3', four => '4', five => '5' },
170                six => '6'
171            },
172        },
173        {   name => 'Edge cases for hash start vs. undefined scalar 3',
174            in   => [
175                '---',
176                'two:',
177                'four: 4',
178                'three: 3',
179                '...',
180            ],
181            out => {
182                two => undef, three => '3', four => '4',
183            },
184        },
185        {   name => 'Edge cases for hash start vs. undefined scalar 4',
186            in   => [
187                '---',
188                'two:',
189                '  four: 4',
190                '  three: 3',
191                '...',
192            ],
193            out => {
194                two => { three => '3', four => '4' },
195            },
196        },
197        {   name => 'Edge cases for hash start vs. undefined scalar 5',
198            in   => [
199                '---',
200                'two:',
201                '- four: 4',
202                '- three: 3',
203                '...',
204            ],
205            out => {
206                two => [ { four => '4' }, { three => '3' } ],
207            },
208        },
209        {   name => 'Space after colon',
210            in   => [ '---', 'spog: ', ' - 1', ' - 2', '...' ],
211            out => { spog => [ 1, 2 ] },
212        },
213        {   name => 'Original YAML::Tiny test',
214            in   => [
215                '---',
216                'invoice: 34843',
217                'date   : 2001-01-23',
218                'bill-to:',
219                '    given  : Chris',
220                '    family : Dumars',
221                '    address:',
222                '        lines: |',
223                '            458 Walkman Dr.',
224                '            Suite #292',
225                '        city    : Royal Oak',
226                '        state   : MI',
227                '        postal  : 48046',
228                'product:',
229                '    - sku         : BL394D',
230                '      quantity    : 4',
231                '      description : Basketball',
232                '      price       : 450.00',
233                '    - sku         : BL4438H',
234                '      quantity    : 1',
235                '      description : Super Hoop',
236                '      price       : 2392.00',
237                'tax  : 251.42',
238                'total: 4443.52',
239                'comments: >',
240                '    Late afternoon is best.',
241                '    Backup contact is Nancy',
242                '    Billsmer @ 338-4338',
243                '...',
244            ],
245            out => {
246                'bill-to' => {
247                    'given'   => 'Chris',
248                    'address' => {
249                        'city'   => 'Royal Oak',
250                        'postal' => '48046',
251                        'lines'  => "458 Walkman Dr.\nSuite #292\n",
252                        'state'  => 'MI'
253                    },
254                    'family' => 'Dumars'
255                },
256                'invoice' => '34843',
257                'date'    => '2001-01-23',
258                'tax'     => '251.42',
259                'product' => [
260                    {   'sku'         => 'BL394D',
261                        'quantity'    => '4',
262                        'price'       => '450.00',
263                        'description' => 'Basketball'
264                    },
265                    {   'sku'         => 'BL4438H',
266                        'quantity'    => '1',
267                        'price'       => '2392.00',
268                        'description' => 'Super Hoop'
269                    }
270                ],
271                'comments' =>
272                  "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338\n",
273                'total' => '4443.52'
274            }
275        },
276
277        # Tests harvested from YAML::Tiny
278        {   in    => ['...'],
279            name  => 'Regression: empty',
280            error => qr{document\s+header\s+not\s+found}
281        },
282        {   in => [
283                '# comment',
284                '...'
285            ],
286            name  => 'Regression: only_comment',
287            error => qr{document\s+header\s+not\s+found}
288        },
289        {   out => undef,
290            in  => [
291                '---',
292                '...'
293            ],
294            name  => 'Regression: only_header',
295            error => qr{Premature\s+end}i,
296        },
297        {   out => undef,
298            in  => [
299                '---',
300                '---',
301                '...'
302            ],
303            name  => 'Regression: two_header',
304            error => qr{Unexpected\s+start}i,
305        },
306        {   out => undef,
307            in  => [
308                '--- ~',
309                '...'
310            ],
311            name => 'Regression: one_undef'
312        },
313        {   out => undef,
314            in  => [
315                '---  ~',
316                '...'
317            ],
318            name => 'Regression: one_undef2'
319        },
320        {   in => [
321                '--- ~',
322                '---',
323                '...'
324            ],
325            name  => 'Regression: two_undef',
326            error => qr{Missing\s+'[.][.][.]'},
327        },
328        {   out => 'foo',
329            in  => [
330                '--- foo',
331                '...'
332            ],
333            name => 'Regression: one_scalar',
334        },
335        {   out => 'foo',
336            in  => [
337                '---  foo',
338                '...'
339            ],
340            name => 'Regression: one_scalar2',
341        },
342        {   in => [
343                '--- foo',
344                '--- bar',
345                '...'
346            ],
347            name  => 'Regression: two_scalar',
348            error => qr{Missing\s+'[.][.][.]'},
349        },
350        {   out => ['foo'],
351            in  => [
352                '---',
353                '- foo',
354                '...'
355            ],
356            name => 'Regression: one_list1'
357        },
358        {   out => [
359                'foo',
360                'bar'
361            ],
362            in => [
363                '---',
364                '- foo',
365                '- bar',
366                '...'
367            ],
368            name => 'Regression: one_list2'
369        },
370        {   out => [
371                undef,
372                'bar'
373            ],
374            in => [
375                '---',
376                '- ~',
377                '- bar',
378                '...'
379            ],
380            name => 'Regression: one_listundef'
381        },
382        {   out => { 'foo' => 'bar' },
383            in  => [
384                '---',
385                'foo: bar',
386                '...'
387            ],
388            name => 'Regression: one_hash1'
389        },
390        {   out => {
391                'foo'  => 'bar',
392                'this' => undef
393            },
394            in => [
395                '---',
396                'foo: bar',
397                'this: ~',
398                '...'
399            ],
400            name => 'Regression: one_hash2'
401        },
402        {   out => {
403                'foo' => [
404                    'bar',
405                    undef,
406                    'baz'
407                ]
408            },
409            in => [
410                '---',
411                'foo:',
412                '  - bar',
413                '  - ~',
414                '  - baz',
415                '...'
416            ],
417            name => 'Regression: array_in_hash'
418        },
419        {   out => {
420                'bar' => { 'foo' => 'bar' },
421                'foo' => undef
422            },
423            in => [
424                '---',
425                'foo: ~',
426                'bar:',
427                '  foo: bar',
428                '...'
429            ],
430            name => 'Regression: hash_in_hash'
431        },
432        {   out => [
433                {   'foo'  => undef,
434                    'this' => 'that'
435                },
436                'foo', undef,
437                {   'foo'  => 'bar',
438                    'this' => 'that'
439                }
440            ],
441            in => [
442                '---',
443                '-',
444                '  foo: ~',
445                '  this: that',
446                '- foo',
447                '- ~',
448                '-',
449                '  foo: bar',
450                '  this: that',
451                '...'
452            ],
453            name => 'Regression: hash_in_array'
454        },
455        {   out => ['foo'],
456            in  => [
457                '---',
458                '- \'foo\'',
459                '...'
460            ],
461            name => 'Regression: single_quote1'
462        },
463        {   out => ['  '],
464            in  => [
465                '---',
466                '- \'  \'',
467                '...'
468            ],
469            name => 'Regression: single_spaces'
470        },
471        {   out => [''],
472            in  => [
473                '---',
474                '- \'\'',
475                '...'
476            ],
477            name => 'Regression: single_null'
478        },
479        {   out => '  ',
480            in  => [
481                '--- "  "',
482                '...'
483            ],
484            name => 'Regression: only_spaces'
485        },
486        {   out => ['first','second'],
487            in  => [
488                '--- ',
489                '- first ',
490                '- second ',
491                '...'
492            ],
493            name => "Space after header for array",
494        },
495        {   out => {'key' => [{'value' => {'key2' => 'value2'}}]},
496            in  => [
497                '--- ',
498                'key: ',
499                '- value: ',
500                '    key2: value2 ',
501                '... '
502            ],
503            name => "Space after header for hash",
504        },
505        {   out => [
506                undef,
507                {   'foo'  => 'bar',
508                    'this' => 'that'
509                },
510                'baz'
511            ],
512            in => [
513                '---',
514                '- ~',
515                '- foo: bar',
516                '  this: that',
517                '- baz',
518                '...'
519            ],
520            name => 'Regression: inline_nested_hash'
521        },
522        {   name => "Unprintables", # This is an ASCII centric test
523            in   => [
524                "---",
525                "- \"\\z\\x01\\x02\\x03\\x04\\x05\\x06\\a\\x08\\t\\n\\v\\f\\r\\x0e\\x0f\"",
526                "- \"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\e\\x1c\\x1d\\x1e\\x1f\"",
527                "- \" !\\\"#\$%&'()*+,-./\"",
528                "- 0123456789:;<=>?",
529                "- '\@ABCDEFGHIJKLMNO'",
530                "- 'PQRSTUVWXYZ[\\]^_'",
531                "- '`abcdefghijklmno'",
532                "- 'pqrstuvwxyz{|}~\177'",
533                "- \200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217",
534                "- \220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237",
535                "- \240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257",
536                "- \260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277",
537                "- \300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317",
538                "- \320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337",
539                "- \340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357",
540                "- \360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",
541                "..."
542            ],
543            out => [
544                "\0\1\2\3\4\5\6\a\b\t\n\13\f\r\16\17",
545                "\20\21\22\23\24\25\26\27\30\31\32\e\34\35\36\37",
546                " !\"#\$%&'()*+,-./",
547                "0123456789:;<=>?",
548                "\@ABCDEFGHIJKLMNO",
549                "PQRSTUVWXYZ[\\]^_",
550                "`abcdefghijklmno",
551                "pqrstuvwxyz{|}~\177",
552                "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217",
553                "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237",
554                "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257",
555                "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277",
556                "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317",
557                "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337",
558                "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357",
559                "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"
560            ],
561        },
562        {   name => 'Quoted hash keys',
563            in   => [
564                '---',
565                '  "quoted": Magic!',
566                '  "\n\t": newline, tab',
567                '...',
568            ],
569            out => {
570                quoted => 'Magic!',
571                "\n\t" => 'newline, tab',
572            },
573        },
574        {   name => 'Empty',
575            in   => [],
576            out  => undef,
577        },
578    );
579
580    plan tests => @SCHEDULE * 5;
581}
582
583sub iter {
584    my $ar = shift;
585    return sub {
586        return shift @$ar;
587    };
588}
589
590for my $test (@SCHEDULE) {
591    my $name = $test->{name};
592    ok my $yaml = TAP::Parser::YAMLish::Reader->new, "$name: Created";
593    isa_ok $yaml, 'TAP::Parser::YAMLish::Reader';
594
595    my $source = join( "\n", @{ $test->{in} } ) . "\n";
596
597    my $iter = iter( $test->{in} );
598    my $got = eval { $yaml->read($iter) };
599
600    my $raw = $yaml->get_raw;
601
602    if ( my $err = $test->{error} ) {
603        unless ( like $@, $err, "$name: Error message" ) {
604            diag "Error: $@\n";
605        }
606        ok !$got, "$name: No result";
607        pass;
608    }
609    else {
610        my $want = $test->{out};
611        unless ( ok !$@, "$name: No error" ) {
612            diag "Error: $@\n";
613        }
614        is_deeply $got, $want,   "$name: Result matches";
615        is $raw,        $source, "$name: Captured source matches";
616    }
617}
618