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

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