OILS / osh / cmd_parse_test.py View on Github | oilshell.org

1493 lines, 815 significant
1#!/usr/bin/env python2
2"""
3cmd_parse_test.py: Tests for cmd_parse.py
4"""
5
6import unittest
7
8from _devbuild.gen.id_kind_asdl import Id, Id_str
9from _devbuild.gen.syntax_asdl import command_e, for_iter_e, pat_e
10from asdl import format as fmt
11from core import error
12from core import state
13from core import test_lib
14from core import ui
15
16from osh import word_
17
18
19def _assertParseMethod(test, code_str, method, expect_success=True):
20 arena = test_lib.MakeArena('<cmd_parse_test>')
21 errfmt = ui.ErrorFormatter()
22 c_parser = test_lib.InitCommandParser(code_str, arena=arena)
23 m = getattr(c_parser, method)
24 try:
25 node = m()
26
27 except error.Parse as e:
28 errfmt.PrettyPrintError(e)
29 if expect_success:
30 test.fail('%r failed' % code_str)
31 node = None
32 else:
33 fmt.PrettyPrint(node)
34 if not expect_success:
35 test.fail('Expected %r to fail ' % code_str)
36
37 return node
38
39
40def _assert_ParseCommandListError(test, code_str):
41 arena = test_lib.MakeArena('<cmd_parse_test>')
42 errfmt = ui.ErrorFormatter()
43 c_parser = test_lib.InitCommandParser(code_str, arena=arena)
44
45 try:
46 node = c_parser._ParseCommandLine()
47 except error.Parse as e:
48 errfmt.PrettyPrintError(e)
49 else:
50 print('UNEXPECTED:')
51 fmt.PrettyPrint(node)
52 test.fail("Expected %r to fail" % code_str)
53
54
55#
56# Successes
57#
58# (These differences might not matter, but preserve the diversity for now)
59
60
61def assertParseSimpleCommand(test, code_str):
62 return _assertParseMethod(test, code_str, 'ParseSimpleCommand')
63
64
65def assertParsePipeline(test, code_str):
66 return _assertParseMethod(test, code_str, 'ParsePipeline')
67
68
69def assertParseAndOr(test, code_str):
70 return _assertParseMethod(test, code_str, 'ParseAndOr')
71
72
73def assert_ParseCommandLine(test, code_str):
74 return _assertParseMethod(test, code_str, '_ParseCommandLine')
75
76
77def assert_ParseCommandList(test, code_str):
78 node = _assertParseMethod(test, code_str, '_ParseCommandList')
79 if len(node.children) == 1:
80 return node.children[0]
81 else:
82 return node
83
84
85def assertParseRedirect(test, code_str):
86 return _assertParseMethod(test, code_str, 'ParseRedirect')
87
88
89#
90# Failures
91#
92
93#def assertFailSimpleCommand(test, code_str):
94# return _assertParseMethod(test, code_str, 'ParseSimpleCommand',
95# expect_success=False)
96#
97#def assertFailCommandLine(test, code_str):
98# return _assertParseMethod(test, code_str, '_ParseCommandLine',
99# expect_success=False)
100
101
102def assertFailCommandList(test, code_str):
103 return _assertParseMethod(test,
104 code_str,
105 '_ParseCommandList',
106 expect_success=False)
107
108
109#def assertFailRedirect(test, code_str):
110# return _assertParseMethod(test, code_str, 'ParseRedirect',
111# expect_success=False)
112
113
114class SimpleCommandTest(unittest.TestCase):
115
116 def testParseSimpleCommand1(self):
117 node = assertParseSimpleCommand(self, 'ls foo')
118 self.assertEqual(2, len(node.words), node.words)
119
120 node = assertParseSimpleCommand(self, 'FOO=bar ls foo')
121 self.assertEqual(2, len(node.words))
122 self.assertEqual(1, len(node.more_env))
123
124 node = assertParseSimpleCommand(
125 self, 'FOO=bar >output.txt SPAM=eggs ls foo')
126 self.assertEqual(2, len(node.words))
127 self.assertEqual(2, len(node.more_env))
128 self.assertEqual(1, len(node.redirects))
129
130 node = assertParseSimpleCommand(
131 self, 'FOO=bar >output.txt SPAM=eggs ls foo >output2.txt')
132 self.assertEqual(2, len(node.words))
133 self.assertEqual(2, len(node.more_env))
134 self.assertEqual(2, len(node.redirects))
135
136 def testMultipleGlobalShAssignments(self):
137 node = assert_ParseCommandList(self, 'ONE=1 TWO=2')
138 self.assertEqual(command_e.ShAssignment, node.tag())
139 self.assertEqual(2, len(node.pairs))
140
141 def testOnlyRedirect(self):
142 # This just touches the file
143 node = assert_ParseCommandList(self, '>out.txt')
144 self.assertEqual(command_e.Simple, node.tag())
145 self.assertEqual(0, len(node.words))
146 self.assertEqual(1, len(node.redirects))
147
148 def testParseRedirectInTheMiddle(self):
149 node = assert_ParseCommandList(self, 'echo >out.txt 1 2 3')
150 self.assertEqual(command_e.Simple, node.tag())
151 self.assertEqual(4, len(node.words))
152 self.assertEqual(1, len(node.redirects))
153
154 def testParseRedirectBeforeShAssignment(self):
155 # Write ENV to a file
156 node = assert_ParseCommandList(self, '>out.txt PYTHONPATH=. env')
157 self.assertEqual(command_e.Simple, node.tag())
158 self.assertEqual(1, len(node.words))
159 self.assertEqual(1, len(node.redirects))
160 self.assertEqual(1, len(node.more_env))
161
162 def testParseAdjacentDoubleQuotedWords(self):
163 node = assertParseSimpleCommand(self,
164 'echo "one"two "three""four" five')
165 self.assertEqual(4, len(node.words))
166
167
168class OldStaticParsing(object):
169
170 def testRedirectsInShAssignment(self):
171 err = _assert_ParseCommandListError(self, 'x=1 >/dev/null')
172 err = _assert_ParseCommandListError(self, 'echo hi; x=1 >/dev/null')
173 err = _assert_ParseCommandListError(self, 'declare x=1 >/dev/null')
174
175 def testParseShAssignment(self):
176 node = assert_ParseCommandList(self, 'local foo=bar spam eggs one=1')
177 self.assertEqual(4, len(node.pairs))
178
179 node = assert_ParseCommandList(self, 'foo=bar')
180 self.assertEqual(1, len(node.pairs))
181
182 # This is not valid since env isn't respected
183 assertFailCommandList(self, 'FOO=bar local foo=$(env)')
184
185 def testExport(self):
186 # This is the old static parsing. Probably need to revisit.
187 return
188 node = assert_ParseCommandList(self, 'export ONE=1 TWO=2 THREE')
189 self.assertEqual(command_e.ShAssignment, node.tag())
190 self.assertEqual(3, len(node.pairs))
191
192 def testReadonly(self):
193 return
194 node = assert_ParseCommandList(self, 'readonly ONE=1 TWO=2 THREE')
195 self.assertEqual(command_e.ShAssignment, node.tag())
196 self.assertEqual(3, len(node.pairs))
197
198
199def assertHereDocToken(test, expected_token_val, node):
200 """A sanity check for some ad hoc tests."""
201 test.assertEqual(1, len(node.redirects))
202 h = node.redirects[0].arg
203 test.assertEqual(expected_token_val, h.stdin_parts[0].tval)
204
205
206class HereDocTest(unittest.TestCase):
207 """NOTE: These ares come from tests/09-here-doc.sh, but add assertions."""
208
209 def testUnquotedHereDoc(self):
210 # Unquoted here docs use the double quoted context.
211 node = assert_ParseCommandLine(self, """\
212cat <<EOF
213$v
214"two
215EOF
216""")
217 self.assertEqual(1, len(node.redirects))
218 h = node.redirects[0].arg
219 # 4 literal parts: VarSub, newline, right ", "two\n"
220 self.assertEqual(4, len(h.stdin_parts))
221
222 def testQuotedHereDocs(self):
223 # Quoted here doc
224 node = assert_ParseCommandLine(self, """\
225cat <<"EOF"
226$v
227"two
228EOF
229""")
230 self.assertEqual(1, len(node.redirects))
231 h = node.redirects[0].arg
232 self.assertEqual(2, len(h.stdin_parts)) # 2 literal parts
233
234 node = assert_ParseCommandLine(
235 self, """\
236cat <<'EOF'
237single-quoted: $var
238EOF
239""")
240 self.assertEqual(1, len(node.redirects))
241 h = node.redirects[0].arg
242 self.assertEqual(1, len(h.stdin_parts)) # 1 line, one literal part
243
244 # \ escape
245 node = assert_ParseCommandLine(
246 self, r"""\
247cat <<EO\F
248single-quoted: $var
249EOF
250""")
251 self.assertEqual(1, len(node.redirects))
252 h = node.redirects[0].arg
253 self.assertEqual(1, len(h.stdin_parts)) # 1 line, one literal part
254
255 def testLeadingTabs(self):
256 node = assert_ParseCommandLine(
257 self, """\
258\tcat <<-EOF
259\tone tab then foo: $foo
260\tEOF
261echo hi
262""")
263 self.assertEqual(node.tag(), command_e.Simple)
264 assertHereDocToken(self, 'one tab then foo: ', node)
265
266 def testHereDocInPipeline(self):
267 # Pipe and command on SAME LINE
268 node = assert_ParseCommandLine(
269 self, """\
270cat <<EOF | tac
271PIPE 1
272PIPE 2
273EOF
274""")
275 self.assertEqual(2, len(node.children))
276 assertHereDocToken(self, 'PIPE 1\n', node.children[0])
277
278 # Pipe command AFTER here doc
279 node = assert_ParseCommandLine(
280 self, """\
281cat <<EOF |
282PIPE 1
283PIPE 2
284EOF
285tac
286""")
287 self.assertEqual(2, len(node.children))
288 assertHereDocToken(self, 'PIPE 1\n', node.children[0])
289
290 def testTwoHereDocsInPipeline(self):
291 # Pipeline with two here docs
292 node = assert_ParseCommandList(
293 self, """\
294cat <<EOF1 | tac <<EOF2
295PIPE A1
296PIPE A2
297EOF1
298PIPE B1
299PIPE B2
300EOF2
301""")
302 self.assertEqual(2, len(node.children))
303 assertHereDocToken(self, 'PIPE A1\n', node.children[0])
304 assertHereDocToken(self, 'PIPE B1\n', node.children[1])
305
306 def testHereDocInAndOrChain(self):
307 # || command AFTER here doc
308 node = assert_ParseCommandLine(
309 self, """\
310cat <<EOF ||
311PIPE 1
312PIPE 2
313EOF
314echo hi
315""")
316 self.assertEqual(2, len(node.children))
317 assertHereDocToken(self, 'PIPE 1\n', node.children[0])
318
319 # && and command on SAME LINE
320 node = assert_ParseCommandLine(
321 self, """\
322cat <<EOF && echo hi
323PIPE 1
324PIPE 2
325EOF
326""")
327 self.assertEqual(2, len(node.children))
328 assertHereDocToken(self, 'PIPE 1\n', node.children[0])
329
330 node = assert_ParseCommandLine(
331 self, """\
332tac <<EOF1 && tac <<EOF2
333PIPE A1
334PIPE A2
335EOF1
336PIPE B1
337PIPE B2
338EOF2
339echo
340""")
341 self.assertEqual(2, len(node.children))
342 assertHereDocToken(self, 'PIPE A1\n', node.children[0])
343 assertHereDocToken(self, 'PIPE B1\n', node.children[1])
344
345 def testHereDocInSequence(self):
346 # PROBLEM: _ParseCommandList vs _ParseCommandLine
347 # _ParseCommandLine only used interactively. _ParseCommandList is used by
348 # ParseFile.
349
350 # command AFTER here doc
351 node = assert_ParseCommandList(
352 self, """\
353cat <<EOF ;
354PIPE 1
355PIPE 2
356EOF
357echo hi
358""")
359 self.assertEqual(node.tag(), command_e.CommandList)
360 self.assertEqual(2, len(node.children), repr(node))
361 assertHereDocToken(self, 'PIPE 1\n', node.children[0].child)
362
363 def testHereDocInSequence2(self):
364 # ; and command on SAME LINE
365 node = assert_ParseCommandList(
366 self, """\
367cat <<EOF ; echo hi
368PIPE 1
369PIPE 2
370EOF
371""")
372 self.assertEqual(node.tag(), command_e.CommandList)
373 self.assertEqual(2, len(node.children))
374 assertHereDocToken(self, 'PIPE 1\n', node.children[0].child)
375
376 def testCommandSubInHereDoc(self):
377 node = assert_ParseCommandLine(
378 self, """\
379cat <<EOF
3801 $(echo 2
381echo 3) 4
382EOF
383""")
384 self.assertEqual(1, len(node.words))
385 self.assertEqual(1, len(node.redirects))
386
387
388class ArrayTest(unittest.TestCase):
389
390 def testArrayLiteral(self):
391 # Empty array
392 node = assert_ParseCommandList(self, 'empty=()')
393 self.assertEqual(['empty'], [p.lhs.name for p in node.pairs])
394 self.assertEqual([], node.pairs[0].rhs.parts[0].words) # No words
395 self.assertEqual(command_e.ShAssignment, node.tag())
396
397 # Array with 3 elements
398 node = assert_ParseCommandList(self, 'array=(a b c)')
399 self.assertEqual(['array'], [p.lhs.name for p in node.pairs])
400 self.assertEqual(3, len(node.pairs[0].rhs.parts[0].words))
401 self.assertEqual(command_e.ShAssignment, node.tag())
402
403 # Array literal can't come after word
404 # Now caught at runtime
405 #assertFailCommandList(self,
406 # 'ls array=(a b c)')
407
408 # Word can't come after array literal
409 assertFailCommandList(self, 'array=(a b c) ls')
410
411 # Two array literals
412 node = assert_ParseCommandList(self, 'array=(a b c); array2=(d e f)')
413 self.assertEqual(2, len(node.children))
414 a2 = node.children[1]
415 self.assertEqual(['array2'], [p.lhs.name for p in a2.pairs])
416
417
418class RedirectTest(unittest.TestCase):
419
420 def testParseRedirects1(self):
421 node = assertParseSimpleCommand(self, '>out.txt cat 1>&2')
422 self.assertEqual(1, len(node.words))
423 self.assertEqual(2, len(node.redirects))
424
425 node = assertParseSimpleCommand(self, ' cat <&3')
426 self.assertEqual(1, len(node.redirects))
427
428 def testParseFilenameRedirect(self):
429 node = assertParseRedirect(self, '>out.txt cat')
430
431 def testDescriptorRedirect(self):
432 node = assertParseRedirect(self, '1>& 2 cat')
433
434 def testHereDoc(self):
435 node = assertParseRedirect(self, """\
436<<EOF cat
437hi
438EOF
439""")
440
441 def testHereDocStrip(self):
442 node = assertParseRedirect(self, """\
443<<-EOF cat
444hi
445EOF
446""")
447
448 def testParseRedirectList(self):
449 node = assertParseRedirect(self, """\
450<<EOF >out.txt cat
451hi
452EOF
453""")
454
455 def testParseCommandWithLeadingRedirects(self):
456 node = assertParseSimpleCommand(self, """\
457<<EOF >out.txt cat
458hi
459EOF
460""")
461 self.assertEqual(1, len(node.words))
462 self.assertEqual(2, len(node.redirects))
463
464 def testClobberRedirect(self):
465 node = assertParseSimpleCommand(self, 'echo hi >| clobbered.txt')
466
467
468class CommandParserTest(unittest.TestCase):
469
470 def testParsePipeline(self):
471 node = assertParsePipeline(self, 'ls foo')
472 self.assertEqual(2, len(node.words))
473
474 node = assertParsePipeline(self, 'ls foo|wc -l')
475 self.assertEqual(2, len(node.children))
476 self.assertEqual(command_e.Pipeline, node.tag())
477
478 node = assertParsePipeline(self, '! echo foo | grep foo')
479 self.assertEqual(2, len(node.children))
480 self.assertEqual(command_e.Pipeline, node.tag())
481 self.assertTrue(node.negated)
482
483 node = assertParsePipeline(self, 'ls foo|wc -l|less')
484 self.assertEqual(3, len(node.children))
485 self.assertEqual(command_e.Pipeline, node.tag())
486
487 _assertParseMethod(self,
488 'ls foo|',
489 'ParsePipeline',
490 expect_success=False)
491
492 def testParsePipelineBash(self):
493 node = assert_ParseCommandList(self, 'ls | cat |& cat')
494 self.assertEqual(command_e.Pipeline, node.tag())
495 self.assertEqual(2, len(node.ops))
496 self.assertEqual(Id.Op_Pipe, node.ops[0].id)
497 self.assertEqual(Id.Op_PipeAmp, node.ops[1].id)
498
499 node = assert_ParseCommandList(self, 'ls |& cat | cat')
500 self.assertEqual(command_e.Pipeline, node.tag())
501 self.assertEqual(2, len(node.ops))
502 self.assertEqual(Id.Op_PipeAmp, node.ops[0].id)
503 self.assertEqual(Id.Op_Pipe, node.ops[1].id)
504
505 node = assert_ParseCommandList(self, 'ls |& cat |& cat')
506 self.assertEqual(command_e.Pipeline, node.tag())
507 self.assertEqual(2, len(node.ops))
508 self.assertEqual(Id.Op_PipeAmp, node.ops[0].id)
509 self.assertEqual(Id.Op_PipeAmp, node.ops[1].id)
510
511 def testParseAndOr(self):
512 node = assertParseAndOr(self, 'ls foo')
513 self.assertEqual(2, len(node.words))
514
515 node = assertParseAndOr(self, 'ls foo|wc -l')
516 self.assertEqual(2, len(node.children))
517 self.assertEqual(command_e.Pipeline, node.tag())
518
519 node = assertParseAndOr(self, 'ls foo || die')
520 self.assertEqual(2, len(node.children))
521 self.assertEqual(command_e.AndOr, node.tag())
522
523 node = assertParseAndOr(self, 'ls foo|wc -l || die')
524 self.assertEqual(2, len(node.children))
525 self.assertEqual(command_e.AndOr, node.tag())
526
527 def testParseCommand(self):
528 c_parser = test_lib.InitCommandParser('ls foo')
529 node = c_parser.ParseCommand()
530 self.assertEqual(2, len(node.words))
531 print(node)
532
533 c_parser = test_lib.InitCommandParser('fun() { echo hi; }')
534 node = c_parser.ParseCommand()
535 print(node)
536 self.assertEqual(command_e.ShFunction, node.tag())
537
538 def test_ParseCommandLine(self):
539 node = assert_ParseCommandLine(self, 'ls foo 2>/dev/null')
540 self.assertEqual(2, len(node.words))
541
542 node = assert_ParseCommandLine(self, 'ls foo|wc -l')
543 self.assertEqual(command_e.Pipeline, node.tag())
544
545 node = assert_ParseCommandLine(self, 'ls foo|wc -l || die')
546 self.assertEqual(command_e.AndOr, node.tag())
547
548 node = assert_ParseCommandLine(self, 'ls foo|wc -l || die; ls /')
549 self.assertEqual(command_e.CommandList, node.tag())
550 self.assertEqual(2, len(node.children)) # two top level things
551
552 def test_ParseCommandList(self):
553 node = assert_ParseCommandList(self, 'ls foo')
554 self.assertEqual(2, len(node.words))
555
556 node = assert_ParseCommandList(self, 'ls foo|wc -l || die; ls /')
557 self.assertEqual(command_e.CommandList, node.tag())
558 self.assertEqual(2, len(node.children))
559
560 node = assert_ParseCommandList(
561 self, """\
562ls foo | wc -l || echo fail ;
563echo bar | wc -c || echo f2
564""")
565 self.assertEqual(command_e.CommandList, node.tag())
566 self.assertEqual(2, len(node.children))
567
568 # TODO: Check that we get (LIST (AND_OR (PIPELINE (COMMAND ...)))) here.
569 # We want all levels.
570
571 def testParseCase(self):
572 # Empty case
573 node = assert_ParseCommandLine(self, """\
574case foo in
575esac
576""")
577 self.assertEqual(command_e.Case, node.tag())
578 self.assertEqual(0, len(node.arms))
579
580 # TODO: Test all these. Probably need to add newlines too.
581 # case foo esac # INVALID
582 # case foo in esac
583 # case foo in foo) esac
584 # case foo in foo) ;; esac
585 # case foo in foo) echo hi ;; esac
586 # case foo in foo) echo hi; ;; esac
587
588 node = assert_ParseCommandLine(
589 self, """\
590case word in
591 foo|foo2|foo3) echo hi ;;
592esac
593""")
594 self.assertEqual(command_e.Case, node.tag())
595 self.assertEqual(1, len(node.arms))
596
597 node = assert_ParseCommandLine(
598 self, """\
599case word in foo) echo one-line ;; esac
600""")
601 self.assertEqual(command_e.Case, node.tag())
602 self.assertEqual(1, len(node.arms))
603
604 node = assert_ParseCommandLine(
605 self, """\
606case word in
607 foo) echo foo ;;
608 bar) echo bar ;;
609esac
610""")
611 self.assertEqual(command_e.Case, node.tag())
612 self.assertEqual(2, len(node.arms))
613
614 node = assert_ParseCommandLine(
615 self, """\
616case word in
617 foo) echo foo ;; # NO TRAILING ;; but trailing ;
618 bar) echo bar ;
619esac
620""")
621 self.assertEqual(command_e.Case, node.tag())
622 self.assertEqual(2, len(node.arms))
623
624 node = assert_ParseCommandLine(
625 self, """\
626case word in
627 foo) echo foo ;; # NO TRAILING ;;
628 bar) echo bar
629esac
630""")
631 self.assertEqual(command_e.Case, node.tag())
632 self.assertEqual(2, len(node.arms))
633
634 def testParseYshCase(self):
635 # Empty case
636 node = assert_ParseCommandLine(self, """\
637case (x) {
638}
639""")
640 self.assertEqual(command_e.Case, node.tag())
641 self.assertEqual(0, len(node.arms))
642
643 node = assert_ParseCommandLine(
644 self, """\
645case (x) {
646 (else) { echo hi; }
647}
648""")
649 self.assertEqual(command_e.Case, node.tag())
650 self.assertEqual(1, len(node.arms))
651 self.assertEqual(pat_e.Else, node.arms[0].pattern.tag())
652
653 node = assert_ParseCommandLine(
654 self, """\
655case (x) {
656(2) | (3) { echo hi; }
657}
658""")
659 self.assertEqual(command_e.Case, node.tag())
660 self.assertEqual(1, len(node.arms))
661 pattern = node.arms[0].pattern
662 self.assertEqual(pat_e.YshExprs, pattern.tag())
663
664 self.assertEqual(2, len(pattern.exprs))
665
666 node = assert_ParseCommandLine(
667 self, """\
668case (x) {
669 bare | x | 'string' { echo hi; }
670}
671""")
672 self.assertEqual(command_e.Case, node.tag())
673 self.assertEqual(1, len(node.arms))
674 pattern = node.arms[0].pattern
675 self.assertEqual(pat_e.Words, pattern.tag())
676 self.assertEqual(3, len(pattern.words))
677
678 node = assert_ParseCommandLine(
679 self, """\
680case (x) {
681 / d+ / { echo space; }
682 /d+/ { echo space2; }
683}
684""")
685 self.assertEqual(command_e.Case, node.tag())
686 self.assertEqual(2, len(node.arms))
687
688 pattern0 = node.arms[0].pattern
689 self.assertEqual(pat_e.Eggex, pattern0.tag())
690
691 pattern1 = node.arms[1].pattern
692 self.assertEqual(pat_e.Eggex, pattern1.tag())
693
694 node = assert_ParseCommandLine(self, """\
695case (x) {
696 word { = x }
697}
698""")
699 self.assertEqual(command_e.Case, node.tag())
700 self.assertEqual(1, len(node.arms))
701
702 arm = node.arms[0]
703 self.assertEqual(Id.Lit_Chars, arm.left.id)
704
705 node = assert_ParseCommandLine(
706 self, """\
707case (x) {
708 /'eggex'/ { = x }
709}
710""")
711 self.assertEqual(command_e.Case, node.tag())
712 self.assertEqual(1, len(node.arms))
713
714 arm = node.arms[0]
715 self.assertEqual(Id.Arith_Slash, arm.left.id)
716
717 node = assert_ParseCommandLine(
718 self, """\
719case (x) {
720 ('expr') { = x }
721}
722""")
723 self.assertEqual(command_e.Case, node.tag())
724 self.assertEqual(1, len(node.arms))
725
726 arm = node.arms[0]
727 self.assertEqual(Id.Op_LParen, arm.left.id)
728
729 node = assert_ParseCommandLine(self, """\
730case (x) {
731 (else) { = x }
732}
733""")
734 self.assertEqual(command_e.Case, node.tag())
735 self.assertEqual(1, len(node.arms))
736
737 arm = node.arms[0]
738 self.assertEqual(Id.Op_LParen, arm.left.id)
739
740 def testParseWhile(self):
741 node = assert_ParseCommandList(
742 self, """\
743while true; do
744 echo hi
745 break
746done
747""")
748
749 node = assert_ParseCommandList(
750 self, """\
751while true # comment
752do # comment
753 echo hi # comment
754 break # comment
755done # comment
756""")
757
758 def testParseUntil(self):
759 node = assert_ParseCommandList(
760 self, """\
761until false; do
762 echo hi
763 break
764done
765""")
766
767 def testParseFor(self):
768 node = assert_ParseCommandList(
769 self, """\
770for i in 1 2 3; do
771 echo $i
772done
773""")
774 self.assertEqual(3, len(node.iterable.words))
775
776 # Don't iterate over anything!
777 node = assert_ParseCommandList(self, """\
778for i in ; do
779 echo $i
780done
781""")
782 self.assertEqual(0, len(node.iterable.words))
783
784 # Iterate over the default
785 node = assert_ParseCommandList(self, """\
786for i; do echo $i; done
787""")
788 self.assertEqual(for_iter_e.Args, node.iterable.tag())
789
790 # Iterate over the default, over multiple lines
791 node = assert_ParseCommandList(self, """\
792for i
793do
794 echo $i
795done
796""")
797 self.assertEqual(for_iter_e.Args, node.iterable.tag())
798
799 def testParseForExpression(self):
800 node = assert_ParseCommandList(
801 self, """\
802for ((i=0; i<5; ++i)); do
803 echo $i
804done
805""")
806 self.assertEqual(Id.Arith_Equal, node.init.op_id)
807 self.assertEqual(Id.Arith_Less, node.cond.op_id)
808 self.assertEqual(Id.Arith_DPlus, node.update.op_id)
809 self.assertEqual(command_e.DoGroup, node.body.tag())
810
811 # Now without the ; OR a newline
812 node = assert_ParseCommandList(
813 self, """\
814for ((i=0; i<5; ++i)) do
815 echo $i
816done
817""")
818 self.assertEqual(Id.Arith_Equal, node.init.op_id)
819 self.assertEqual(Id.Arith_Less, node.cond.op_id)
820 self.assertEqual(Id.Arith_DPlus, node.update.op_id)
821 self.assertEqual(command_e.DoGroup, node.body.tag())
822
823 node = assert_ParseCommandList(self, """\
824for ((;;)); do
825 echo $i
826done
827""")
828 self.assertEqual(command_e.DoGroup, node.body.tag())
829
830 def testParseCommandSub(self):
831 # Two adjacent command subs
832 node = assertParseSimpleCommand(self, 'echo $(echo 12)$(echo 34)')
833 self.assertEqual(2, len(node.words))
834
835 # Two adjacent command subs, quoted
836 node = assertParseSimpleCommand(self, 'echo "$(echo 12)$(echo 34)"')
837 self.assertEqual(2, len(node.words))
838
839 def testParseTildeSub(self):
840 node = assert_ParseCommandList(
841 self,
842 "ls ~ ~root ~/src ~/src/foo ~root/src ~weird!name/blah!blah ")
843
844 def testParseDBracket(self):
845 node = assert_ParseCommandList(self, '[[ $# -gt 1 ]]')
846
847 # Bash allows embedded newlines in some places, but not all
848 node = assert_ParseCommandList(self, """\
849[[ $# -gt 1 &&
850
851foo ]]""")
852
853 # Newline needs to be Id.Op_Newline!
854 node = assert_ParseCommandList(
855 self, """\
856if [[ $# -gt 1 ]]
857then
858 echo hi
859fi
860""")
861
862 # Doh, technically this works!
863 # [[ =~ =~ =~ ]]; echo $?
864 # 0
865
866 def testParseDParen(self):
867 node = assert_ParseCommandList(self, '(( 1 + 2 ))')
868
869 def testParseDBracketRegex(self):
870 node = assert_ParseCommandList(self, '[[ foo =~ foo ]]')
871 self.assertEqual(Id.BoolBinary_EqualTilde, node.expr.op_id)
872
873 node = assert_ParseCommandList(self, '[[ foo =~ (foo|bar) ]]')
874 self.assertEqual(Id.BoolBinary_EqualTilde, node.expr.op_id)
875 right = node.expr.right
876 self.assertEqual(5, len(right.parts))
877 self.assertEqual('(', right.parts[0].tval)
878
879 # TODO: Implement BASH_REGEX_CHARS
880 return
881 node = assert_ParseCommandList(self, '[[ "< >" =~ (< >) ]]')
882 self.assertEqual(Id.BoolBinary_EqualTilde, node.expr.op_id)
883
884 node = assert_ParseCommandList(self, '[[ "ba ba" =~ ([a b]+) ]]')
885 self.assertEqual(Id.BoolBinary_EqualTilde, node.expr.op_id)
886
887 def testParseIf(self):
888 node = assert_ParseCommandList(self, 'if true; then echo yes; fi')
889 # Subshell in condition
890 node = assert_ParseCommandList(self, 'if (true); then echo yes; fi')
891
892 def testParseFunction(self):
893 node = assert_ParseCommandList(self, 'foo() { echo hi; }')
894
895 node = assert_ParseCommandList(self, 'foo() ( echo hi )')
896 node = assert_ParseCommandList(self,
897 'foo() for i in x; do echo $i; done')
898
899 # KSH FUNCTION
900 node = assert_ParseCommandList(self, 'function foo { echo hi; }')
901 node = assert_ParseCommandList(self, 'function foo () { echo hi; }')
902
903 node = assert_ParseCommandList(self, 'function foo() ( echo hi )')
904 node = assert_ParseCommandList(
905 self, 'function foo() for i in x; do echo $i; done')
906
907 # No () is OK here!
908 node = assert_ParseCommandList(
909 self, 'function foo for i in x; do echo $i; done')
910
911 # Redirects
912 node = assert_ParseCommandList(self,
913 'foo() { echo hi; } 1>&2 2>/dev/null')
914 self.assertEqual(command_e.BraceGroup, node.body.tag())
915 self.assertEqual(2, len(node.body.redirects))
916
917 def testParseKeyword(self):
918 # NOTE: It chooses the longest match, which is Lit_Chars>
919 node = assert_ParseCommandList(self, 'ifFOO')
920
921
922class NestedParensTest(unittest.TestCase):
923 """Test the hard $() and () nesting.
924
925 Meanings of ):
926
927 ( echo x ) # subshell (cmd_parse)
928 echo $(echo x) # command substitution (word_parse)
929 (( )) # end arith command (cmd_parse)
930 $(( )) # end arith sub (word_parse))
931 a=(1 2 3) # array literal and assoc array literal
932 a[1*(2+3)]=x # grouping in arith context
933 fun() { echo x ; } # function def
934
935 case x in x) echo x ;; esac # case, with balanced or unbalanced
936 case x in (x) echo x ;; esac
937 """
938
939 def testParseSubshell(self):
940 node = assert_ParseCommandLine(self, '(cd /; echo PWD 1); echo PWD 2')
941 self.assertEqual(2, len(node.children))
942 self.assertEqual(command_e.CommandList, node.tag())
943
944 def testParseBraceGroup(self):
945 node = assert_ParseCommandLine(self, '{ cd /; echo PWD; }')
946 self.assertEqual(2, len(node.children))
947 self.assertEqual(command_e.BraceGroup, node.tag())
948
949 node = assert_ParseCommandLine(self, '{ cd /; echo PWD; }; echo PWD')
950 self.assertEqual(2, len(node.children))
951 self.assertEqual(command_e.CommandList, node.tag())
952
953 def testUnquotedComSub(self):
954 # CommandSub with two Literal instances surrounding it
955 node = assertParseSimpleCommand(self, 'echo ab$(echo hi)cd ef')
956 self.assertEqual(3, len(node.words))
957
958 def testNestedComSub(self):
959 node = assertParseSimpleCommand(self,
960 'echo $(one$(echo two)one) three')
961 self.assertEqual(3, len(node.words))
962
963 def testArithSubWithin(self):
964 # Within com sub
965 node = assertParseSimpleCommand(self, 'echo $(echo $((1+2)))')
966 self.assertEqual(command_e.Simple, node.tag())
967 self.assertEqual(2, len(node.words))
968
969 # Within subshell
970 node = assert_ParseCommandList(self, '(echo $((1+2)))')
971 self.assertEqual(command_e.Subshell, node.tag())
972 self.assertEqual(command_e.Simple, node.child.tag())
973
974 def testArithGroupingWithin(self):
975 # Within com sub
976 node = assertParseSimpleCommand(self, 'echo $(echo $((1*(2+3))) )')
977 self.assertEqual(command_e.Simple, node.tag())
978 self.assertEqual(2, len(node.words))
979
980 # Within subshell
981 node = assert_ParseCommandList(self, '(echo $((1*(2+3))) )')
982 self.assertEqual(command_e.Subshell, node.tag())
983 self.assertEqual(command_e.Simple, node.child.tag())
984
985 def testLhsArithGroupingWithin(self):
986 # Within Arith sub
987 node = assertParseSimpleCommand(self, 'echo $((a[1*(2+3)]=x))')
988 self.assertEqual(2, len(node.words))
989
990 # Within Command Sub -- NOT IMPLEMENTED
991 return
992 node = assertParseSimpleCommand(self, 'echo $(a[1*(2+3)]=x)')
993 self.assertEqual(2, len(node.words))
994
995 def testShFunctionWithin(self):
996 node = assert_ParseCommandList(self, 'echo $(fun() { echo hi; }; fun)')
997 self.assertEqual(command_e.Simple, node.tag())
998 self.assertEqual(2, len(node.words))
999
1000 node = assert_ParseCommandList(self, '(fun() { echo hi; }; fun)')
1001 self.assertEqual(command_e.Subshell, node.tag())
1002 self.assertEqual(command_e.CommandList, node.child.tag())
1003
1004 def testArrayLiteralWithin(self):
1005 node = assert_ParseCommandList(self, 'echo $(array=(a b c))')
1006 self.assertEqual(command_e.Simple, node.tag())
1007 self.assertEqual(2, len(node.words))
1008
1009 node = assert_ParseCommandList(self, '(array=(a b c))')
1010 self.assertEqual(command_e.Subshell, node.tag())
1011 self.assertEqual(command_e.ShAssignment, node.child.tag())
1012
1013 def testSubshellWithinComSub(self):
1014 node = assert_ParseCommandList(
1015 self,
1016 'echo one; echo $( (cd /; echo subshell_PWD); echo comsub_PWD); echo two'
1017 )
1018 self.assertEqual(command_e.CommandList, node.tag())
1019 self.assertEqual(3, len(node.children)) # 3 echo statements
1020
1021 # TODO: Need a way to test the literal value of a word
1022 #words = [w.UnquotedLiteralValue() for w in node.children[2].words]
1023 #print(words)
1024
1025 def testCaseWithinComSub(self):
1026 node = assert_ParseCommandList(
1027 self, 'echo $( case foo in one) echo comsub;; esac)')
1028 self.assertEqual(2, len(node.words))
1029
1030 node = assert_ParseCommandList(
1031 self, """\
1032echo $(
1033case foo in one) echo comsub1;; esac
1034case bar in two) echo comsub2;; esac
1035)
1036""")
1037 self.assertEqual(2, len(node.words))
1038
1039 def testComsubWithinCaseWithinComSub(self):
1040 # Comsub within case within comsub
1041 node = assert_ParseCommandList(
1042 self,
1043 'echo one; echo $( case one in $(echo one)) echo $(comsub);; esac ); echo two'
1044 )
1045 self.assertEqual(command_e.CommandList, node.tag())
1046 # Top level should have 3 echo statements
1047 self.assertEqual(3, len(node.children))
1048
1049 def testComSubWithinDoubleQuotes(self):
1050 # CommandSub with two Literal instances surrounding it
1051 node = assertParseSimpleCommand(self,
1052 'echo "double $(echo hi) quoted" two')
1053 self.assertEqual(3, len(node.words))
1054
1055 def testEmptyCaseWithinSubshell(self):
1056 node = assert_ParseCommandList(self, """\
1057( case foo in
1058 esac
1059)
1060""")
1061 self.assertEqual(command_e.Subshell, node.tag())
1062
1063 def testBalancedCaseWithin(self):
1064 # With leading ( in case. This one doesn't cause problems! We don't need
1065 # the MaybeUnreadOne() lexer hack.
1066 node = assert_ParseCommandList(
1067 self, """\
1068$( case foo in
1069 (one) echo hi ;;
1070 esac
1071)
1072""")
1073 self.assertEqual(command_e.Simple, node.tag())
1074
1075 node = assert_ParseCommandList(
1076 self, """\
1077( case foo in
1078 (one) echo hi ;;
1079 esac
1080)
1081""")
1082 self.assertEqual(command_e.Subshell, node.tag())
1083
1084 def testUnbalancedCaseWithin(self):
1085 # With leading ( in case. This one doesn't cause problems! We don't need
1086 # the MaybeUnreadOne() lexer hack.
1087 node = assert_ParseCommandList(
1088 self, """\
1089$( case foo in
1090 one) echo hi ;;
1091 esac
1092)
1093""")
1094 self.assertEqual(command_e.Simple, node.tag())
1095
1096 node = assert_ParseCommandList(
1097 self, """\
1098( case foo in
1099 one) echo hi ;;
1100 esac
1101)
1102""")
1103 self.assertEqual(command_e.Subshell, node.tag())
1104
1105 def testForExpressionWithin(self):
1106 # With leading ( in case. This one doesn't cause problems! We don't need
1107 # the MaybeUnreadOne() lexer hack.
1108 node = assert_ParseCommandList(
1109 self, """\
1110$( for ((i=0; i<3; ++i)); do
1111 echo hi
1112 done
1113)
1114""")
1115 self.assertEqual(command_e.Simple, node.tag())
1116
1117 node = assert_ParseCommandList(
1118 self, """\
1119( for ((i=0; i<3; ++i)); do
1120 echo hi
1121 done
1122)
1123""")
1124 self.assertEqual(command_e.Subshell, node.tag())
1125
1126
1127class RealBugsTest(unittest.TestCase):
1128
1129 def testGitBug(self):
1130 # Original bug from git codebase. Case in subshell.
1131 node = assert_ParseCommandList(
1132 self, """\
1133( cd "$PACKDIR" &&
1134 for e in $existing
1135 do
1136 case " $fullbases " in
1137 *" $e "*) ;;
1138 *) rm -f "$e.pack" "$e.idx" "$e.keep" ;;
1139 esac
1140 done
1141)
1142""")
1143 self.assertEqual(command_e.Subshell, node.tag())
1144
1145 def testParseCase3(self):
1146 # Bug from git codebase. NOT a comment token.
1147 node = assert_ParseCommandLine(
1148 self, """\
1149case "$fd,$command" in
1150 3,#*|3,)
1151 # copy comments
1152 ;;
1153esac
1154""")
1155 self.assertEqual(command_e.Case, node.tag())
1156
1157 def testGitComment(self):
1158 # ;# is a comment! Gah.
1159 # Conclusion: Comments are NOT LEXICAL. They are part of word parsing.
1160
1161 node = assert_ParseCommandList(
1162 self, """\
1163. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
1164""")
1165 self.assertEqual(command_e.Sentence, node.tag())
1166 self.assertEqual(2, len(node.child.words))
1167
1168 # This is NOT a comment
1169 node = assert_ParseCommandList(self, """\
1170echo foo#bar
1171""")
1172 self.assertEqual(command_e.Simple, node.tag())
1173 self.assertEqual(2, len(node.words))
1174 _, s, _ = word_.StaticEval(node.words[1])
1175 self.assertEqual('foo#bar', s)
1176
1177 # This is a comment
1178 node = assert_ParseCommandList(self, """\
1179echo foo #comment
1180""")
1181 self.assertEqual(command_e.Simple, node.tag())
1182 self.assertEqual(2, len(node.words))
1183 _, s, _ = word_.StaticEval(node.words[1])
1184 self.assertEqual('foo', s)
1185
1186 # Empty comment
1187 node = assert_ParseCommandList(self, """\
1188echo foo #
1189""")
1190 self.assertEqual(command_e.Simple, node.tag())
1191 self.assertEqual(2, len(node.words))
1192 _, s, _ = word_.StaticEval(node.words[1])
1193 self.assertEqual('foo', s)
1194
1195 def testChromeIfSubshell(self):
1196 node = assert_ParseCommandList(
1197 self, """\
1198if true; then (
1199 echo hi
1200)
1201fi
1202""")
1203 self.assertEqual(command_e.If, node.tag())
1204
1205 node = assert_ParseCommandList(
1206 self, """\
1207while true; do {
1208 echo hi
1209 break
1210} done
1211""")
1212 self.assertEqual(command_e.WhileUntil, node.tag())
1213 self.assertEqual(Id.KW_While, node.keyword.id)
1214
1215 node = assert_ParseCommandList(
1216 self, """\
1217if true; then (
1218 echo hi
1219) fi
1220""")
1221 self.assertEqual(command_e.If, node.tag())
1222
1223 # Related: two fi's in a row, found in Chrome configure. Compound commands
1224 # are special; don't need newlines.
1225 node = assert_ParseCommandList(
1226 self, """\
1227if true; then
1228 if true; then
1229 echo hi
1230 fi fi
1231echo hi
1232""")
1233 self.assertEqual(command_e.CommandList, node.tag())
1234
1235 def testBackticks(self):
1236 # Another empty command sub
1237 node = assert_ParseCommandList(self, """\
1238echo $()
1239""")
1240
1241 # Simplest case
1242 node = assert_ParseCommandList(self, """\
1243echo ``
1244""")
1245
1246 # Found in the wild.
1247 # Just a comment trick found in sandstorm
1248 node = assert_ParseCommandList(
1249 self, """\
1250cmd \
1251 flag `# comment` \
1252 flag2
1253""")
1254
1255 # Empty should be allowed
1256 node = assert_ParseCommandList(self, """\
1257FOO="bar"`
1258 `"baz"
1259""")
1260
1261 def testQuineDb(self):
1262 # Need to handle the DOLLAR_SQ lex state
1263 node = assert_ParseCommandList(
1264 self, r"""\
1265case foo in
1266$'\'')
1267 ret+="\\"\'
1268 ;;
1269esac
1270""")
1271 self.assertEqual(command_e.Case, node.tag())
1272
1273 node = assert_ParseCommandList(self, r"""\
1274$'abc\ndef'
1275""")
1276 self.assertEqual(command_e.Simple, node.tag())
1277 self.assertEqual(1, len(node.words))
1278 w = node.words[0]
1279 self.assertEqual(1, len(w.parts))
1280 p = w.parts[0]
1281 self.assertEqual(3, len(p.tokens))
1282 self.assertEqual(Id.Char_Literals, p.tokens[0].id)
1283 self.assertEqual(Id.Char_OneChar, p.tokens[1].id)
1284 self.assertEqual(Id.Char_Literals, p.tokens[2].id)
1285
1286 def testArithConstants(self):
1287 # Found in Gherkin
1288 node = assert_ParseCommandList(
1289 self, r"""\
1290 [[ -n "${marks[${tag_marker}002${cons_ptr}]}" ]];
1291""")
1292 # Dynamic constant
1293 node = assert_ParseCommandList(self, r"""\
1294echo $(( 0x$foo ))
1295""")
1296
1297 def testBacktickCommentHack(self):
1298 # Found in sandstorm.
1299 # The problem here is that the comment goes to the end of the line, which
1300 # eats up the closing backtick! We could change the mode of the lexer
1301 # inside a command sub, or possibly just ignore this use case.
1302 return
1303
1304 node = assert_ParseCommandList(
1305 self, r"""\
1306openssl \
1307 -newkey rsa:4096 `# Create a new RSA key of length 4096 bits.` \
1308 `# Sandcats just needs the CN= (common name) in the request.` \
1309 -subj "/CN=*.${SS_HOSTNAME}/"
1310""")
1311
1312 def testArrayLiteralFromSetup(self):
1313 # Found in setup.shl/bin/setup -- this is the "Parsing Bash is
1314 # Undecidable" problem.
1315 err = _assert_ParseCommandListError(
1316 self, """\
1317errcmd=( "${SETUP_STATE[$err.cmd]}" )
1318""")
1319
1320 # Double quotes fix it.
1321 node = assert_ParseCommandList(
1322 self, r"""\
1323errcmd=( "${SETUP_STATE["$err.cmd"]}" )
1324""")
1325
1326
1327class ErrorLocationsTest(unittest.TestCase):
1328
1329 def testCommand(self):
1330 """Enumerating errors in cmd_parse.py."""
1331
1332 err = _assert_ParseCommandListError(self, 'ls <')
1333
1334 err = _assert_ParseCommandListError(self, 'ls < <')
1335
1336 # Word parse error in command parser
1337 err = _assert_ParseCommandListError(self, r'echo foo$(ls <)bar')
1338
1339 err = _assert_ParseCommandListError(self, r'BAD_ENV=(1 2 3) ls')
1340
1341 # This needs more context
1342 err = _assert_ParseCommandListError(
1343 self, 'for ((i=1; i<)); do echo $i; done')
1344
1345 err = _assert_ParseCommandListError(
1346 self, 'for ((i=1; i<5; ++i)) OOPS echo $i; ERR')
1347
1348 # After semi
1349 err = _assert_ParseCommandListError(
1350 self, 'for ((i=1; i<5; ++i)); OOPS echo $i; ERR')
1351
1352 err = _assert_ParseCommandListError(
1353 self, 'for $bad in 1 2; do echo hi; done')
1354
1355 err = _assert_ParseCommandListError(self, 'for foo BAD')
1356
1357 err = _assert_ParseCommandListError(self, 'if foo; then echo hi; z')
1358
1359 err = _assert_ParseCommandListError(self,
1360 'foo$(invalid) () { echo hi; }')
1361
1362 def testErrorInHereDoc(self):
1363 return
1364 # Here doc body. Hm this should be failing. Does it just fail to get
1365 # filled?
1366 err = _assert_ParseCommandListError(self, """cat <<EOF
1367$(echo <)
1368EOF
1369""")
1370 return
1371
1372 def testBool(self):
1373 """Enumerating errors in bool_parse.py."""
1374 err = _assert_ParseCommandListError(self, '[[ foo bar ]]')
1375 err = _assert_ParseCommandListError(self, '[[ foo -eq ]]')
1376
1377 # error in word
1378 err = _assert_ParseCommandListError(self, '[[ foo$(echo <) -eq foo ]]')
1379
1380 return
1381 # NOTE: This was disabled because of escaping.
1382 # Invalid regex
1383 err = _assert_ParseCommandListError(self, '[[ foo =~ \( ]]')
1384
1385 def testArith(self):
1386 """Enumerating errors in arith_parse.py."""
1387 err = _assert_ParseCommandListError(self, '(( 1 + ))')
1388
1389 def testArraySyntax(self):
1390 err = _assert_ParseCommandListError(self, 'A= (1 2)')
1391
1392 def testEofInDoubleQuoted(self):
1393 err = _assert_ParseCommandListError(self, 'foo="" echo "bar ')
1394
1395 def testQuotesInFunctionName(self):
1396 err = _assert_ParseCommandListError(
1397 self, """\
1398 foo"bar" () {
1399 echo hi
1400 }
1401 """)
1402
1403 def testForLoopName(self):
1404 err = _assert_ParseCommandListError(
1405 self, """\
1406 for [ i = 1; i < 10; i++ ]
1407 """)
1408 err = _assert_ParseCommandListError(self, """\
1409 for = in a
1410 """)
1411
1412 def testHereDocCommandSub(self):
1413 # Originally from spec/09-here-doc.sh.
1414 err = _assert_ParseCommandListError(
1415 self, """\
1416for x in 1 2 $(cat <<EOF
1417THREE
1418EOF); do
1419 echo for word $x
1420done
1421""")
1422
1423 def testForLoopEof(self):
1424 err = _assert_ParseCommandListError(self, "for x in 1 2 $(")
1425
1426
1427class ParserInteractionsTest(unittest.TestCase):
1428
1429 def _dumpLexerState(self, lexer):
1430 print("----")
1431 print(lexer.line_lexer.src_line.content)
1432 print(" " * lexer.line_lexer.line_pos + "^ We are here")
1433 print("----")
1434
1435 def testBraceGroup(self):
1436 code_str = '{ echo hello; } '
1437
1438 c_parser = test_lib.InitCommandParser(code_str)
1439 lexer = c_parser.lexer
1440
1441 c_parser.ParseBraceGroup()
1442
1443 if 0:
1444 self._dumpLexerState(lexer)
1445
1446 # We should be at the end of the line:
1447 # '{ echo hello; } '
1448 # ^ Which is here
1449 self.assertEqual(len(lexer.line_lexer.src_line.content),
1450 lexer.line_lexer.line_pos)
1451
1452 next_id = c_parser.w_parser.LookPastSpace()
1453 self.assertEqual(next_id, Id.Unknown_Tok, Id_str(next_id))
1454
1455 def testYSHBraceGroup(self):
1456 code_str = '{ echo hello } '
1457
1458 c_parser = test_lib.InitCommandParser(code_str)
1459 c_parser.parse_opts = state.MakeOilOpts() # place parser in YSH mode
1460 lexer = c_parser.lexer
1461
1462 c_parser.ParseBraceGroup()
1463
1464 if 0:
1465 self._dumpLexerState(lexer)
1466
1467 self.assertEqual(len(lexer.line_lexer.src_line.content),
1468 lexer.line_lexer.line_pos)
1469
1470 next_id = c_parser.w_parser.LookPastSpace()
1471 self.assertEqual(next_id, Id.Unknown_Tok)
1472
1473 def testCmd2Expr2Cmd(self):
1474 code_str = '{ = hello } '
1475
1476 c_parser = test_lib.InitCommandParser(code_str)
1477 c_parser.parse_opts = state.MakeOilOpts() # place parser in YSH mode
1478 lexer = c_parser.lexer
1479
1480 c_parser.ParseBraceGroup()
1481
1482 if 0:
1483 self._dumpLexerState(lexer)
1484
1485 self.assertEqual(len(lexer.line_lexer.src_line.content),
1486 lexer.line_lexer.line_pos)
1487
1488 next_id = c_parser.w_parser.LookPastSpace()
1489 self.assertEqual(next_id, Id.Unknown_Tok)
1490
1491
1492if __name__ == '__main__':
1493 unittest.main()