| 1 | #!/usr/bin/env python2
 | 
| 2 | # Copyright 2016 Andy Chu. All rights reserved.
 | 
| 3 | # Licensed under the Apache License, Version 2.0 (the "License");
 | 
| 4 | # you may not use this file except in compliance with the License.
 | 
| 5 | # You may obtain a copy of the License at
 | 
| 6 | #
 | 
| 7 | #   http://www.apache.org/licenses/LICENSE-2.0
 | 
| 8 | """
 | 
| 9 | word_eval_test.py: Tests for word_eval.py
 | 
| 10 | """
 | 
| 11 | from __future__ import print_function
 | 
| 12 | 
 | 
| 13 | import unittest
 | 
| 14 | 
 | 
| 15 | from core import error
 | 
| 16 | from core import test_lib
 | 
| 17 | from core import util
 | 
| 18 | from frontend import consts
 | 
| 19 | from osh.cmd_parse_test import assertParseSimpleCommand
 | 
| 20 | 
 | 
| 21 | 
 | 
| 22 | def InitEvaluator():
 | 
| 23 |     word_ev = test_lib.InitWordEvaluator()
 | 
| 24 |     test_lib.SetLocalString(word_ev.mem, 'x', '- -- ---')
 | 
| 25 |     test_lib.SetLocalString(word_ev.mem, 'y', 'y yy')
 | 
| 26 |     test_lib.SetLocalString(word_ev.mem, 'empty', '')
 | 
| 27 |     test_lib.SetLocalString(word_ev.mem, 'binding', 'spam=eggs')
 | 
| 28 |     test_lib.SetLocalString(word_ev.mem, 'binding_with_spaces',
 | 
| 29 |                             'x=green eggs and ham')
 | 
| 30 | 
 | 
| 31 |     word_ev.mem.SetArgv(['x', 'foo', 'spam=eggs'])
 | 
| 32 |     return word_ev
 | 
| 33 | 
 | 
| 34 | 
 | 
| 35 | class RegexTest(unittest.TestCase):
 | 
| 36 | 
 | 
| 37 |     def testSplitAssignArg(self):
 | 
| 38 |         CASES = [
 | 
| 39 |             # var name, op, value
 | 
| 40 |             ('s', ['s', '', '']),
 | 
| 41 |             ('value', ['value', '', '']),
 | 
| 42 |             ('s!', None),
 | 
| 43 |             ('!', None),
 | 
| 44 |             ('=s', None),
 | 
| 45 |             ('s=', ['s', '=', '']),
 | 
| 46 |             ('s=val', ['s', '=', 'val']),
 | 
| 47 |             ('s=+', ['s', '=', '+']),
 | 
| 48 |             ('s+=val!', ['s', '+=', 'val!']),
 | 
| 49 |             ('s+=+', ['s', '+=', '+']),
 | 
| 50 |         ]
 | 
| 51 | 
 | 
| 52 |         for s, expected in CASES:
 | 
| 53 |             actual = util.RegexSearch(consts.ASSIGN_ARG_RE, s)
 | 
| 54 |             if actual is None:
 | 
| 55 |                 self.assertEqual(expected, actual)  # no match
 | 
| 56 |             else:
 | 
| 57 |                 _, var_name, _, op, value = actual
 | 
| 58 |                 self.assertEqual(expected, [var_name, op, value])
 | 
| 59 | 
 | 
| 60 |     def testTestV(self):
 | 
| 61 |         CASES = [
 | 
| 62 |             ('mystr', ['mystr', '']),
 | 
| 63 |             ('myarray[1]', ['myarray', '1']),
 | 
| 64 |             ('assoc[name]', ['assoc', 'name']),
 | 
| 65 |             # Should we allow spaces?
 | 
| 66 |             ('assoc[name] ', None),
 | 
| 67 |             ('assoc[name]]', None),
 | 
| 68 |             ('assoc[name]z', None),
 | 
| 69 |             ('assoc[name', None),
 | 
| 70 |             ('not-var', None),
 | 
| 71 |         ]
 | 
| 72 | 
 | 
| 73 |         for s, expected in CASES:
 | 
| 74 |             actual = util.RegexSearch(consts.TEST_V_RE, s)
 | 
| 75 |             if actual is None:
 | 
| 76 |                 self.assertEqual(expected, actual)  # no match
 | 
| 77 |             else:
 | 
| 78 |                 print(actual)
 | 
| 79 |                 _, name, _, index = actual
 | 
| 80 |                 self.assertEqual(expected, [name, index])
 | 
| 81 | 
 | 
| 82 | 
 | 
| 83 | class WordEvalTest(unittest.TestCase):
 | 
| 84 | 
 | 
| 85 |     def testEvalWordSequence_Errors(self):
 | 
| 86 |         CASES = [
 | 
| 87 |             'readonly a[x]=1',
 | 
| 88 |             'readonly $binding a[x]=1',
 | 
| 89 |             # There's no word elision!  This will be a parse error
 | 
| 90 |             'declare $empty',
 | 
| 91 |         ]
 | 
| 92 | 
 | 
| 93 |         for case in CASES:
 | 
| 94 |             print()
 | 
| 95 |             print('\t%s' % case)
 | 
| 96 |             node = assertParseSimpleCommand(self, case)
 | 
| 97 |             ev = InitEvaluator()
 | 
| 98 |             try:
 | 
| 99 |                 argv = ev.EvalWordSequence2(node.words, allow_assign=True)
 | 
| 100 |             except error.FatalRuntime:
 | 
| 101 |                 pass
 | 
| 102 |             else:
 | 
| 103 |                 self.fail("%r should have raised ParseError", case)
 | 
| 104 | 
 | 
| 105 |     def testEvalWordSequence(self):
 | 
| 106 |         node = assertParseSimpleCommand(self, 'ls foo')
 | 
| 107 |         self.assertEqual(2, len(node.words), node.words)
 | 
| 108 |         print()
 | 
| 109 |         print()
 | 
| 110 | 
 | 
| 111 |         CASES = [
 | 
| 112 |             'ls [$x] $y core/a*.py',
 | 
| 113 |             'local a=1',
 | 
| 114 | 
 | 
| 115 |             # What to do about these?
 | 
| 116 |             # Resolve second word then?
 | 
| 117 |             'builtin local a=1',
 | 
| 118 |             'command local a=1',
 | 
| 119 |             'typeset -"$@"',
 | 
| 120 |             # array=(b c)',
 | 
| 121 |             'local a=(1 2) "$@"',  # static then dynamic
 | 
| 122 |             'readonly "$@" a=(1 2)',  # dynamic then static
 | 
| 123 |             'declare -rx foo=bar spam=eggs a=(1 2)',
 | 
| 124 |             'declare $binding',
 | 
| 125 |             'declare $binding_with_spaces',
 | 
| 126 | 
 | 
| 127 |             # This can be parsed, but the builtin should reject it
 | 
| 128 |             'export a=(1 2)',
 | 
| 129 |             'export A=(["k"]=v)',
 | 
| 130 | 
 | 
| 131 |             # Hard test cases:
 | 
| 132 |             #
 | 
| 133 |             # command export foo=bar
 | 
| 134 |             # builtin export foo=bar
 | 
| 135 |             #
 | 
| 136 |             # b=builtin c=command e=export binding='foo=bar'
 | 
| 137 |             # $c $e $binding
 | 
| 138 |             # $b $e $binding
 | 
| 139 |         ]
 | 
| 140 | 
 | 
| 141 |         for case in CASES:
 | 
| 142 |             print()
 | 
| 143 |             print('\t%s' % case)
 | 
| 144 |             node = assertParseSimpleCommand(self, case)
 | 
| 145 |             ev = InitEvaluator()
 | 
| 146 |             argv = ev.EvalWordSequence2(node.words, allow_assign=True)
 | 
| 147 | 
 | 
| 148 |             print()
 | 
| 149 |             print('\tcmd_value:')
 | 
| 150 |             print(argv)
 | 
| 151 |             print()
 | 
| 152 | 
 | 
| 153 | 
 | 
| 154 | if __name__ == '__main__':
 | 
| 155 |     unittest.main()
 |