Home | History | Annotate | Download | only in idle_test
      1 """Unittest for idlelib.hyperparser.py."""
      2 import unittest
      3 from test.support import requires
      4 from tkinter import Tk, Text
      5 from idlelib.editor import EditorWindow
      6 from idlelib.hyperparser import HyperParser
      7 
      8 class DummyEditwin:
      9     def __init__(self, text):
     10         self.text = text
     11         self.indentwidth = 8
     12         self.tabwidth = 8
     13         self.context_use_ps1 = True
     14         self.num_context_lines = 50, 500, 1000
     15 
     16     _build_char_in_string_func = EditorWindow._build_char_in_string_func
     17     is_char_in_string = EditorWindow.is_char_in_string
     18 
     19 
     20 class HyperParserTest(unittest.TestCase):
     21     code = (
     22             '"""This is a module docstring"""\n'
     23             '# this line is a comment\n'
     24             'x = "this is a string"\n'
     25             "y = 'this is also a string'\n"
     26             'l = [i for i in range(10)]\n'
     27             'm = [py*py for # comment\n'
     28             '       py in l]\n'
     29             'x.__len__\n'
     30             "z = ((r'asdf')+('a')))\n"
     31             '[x for x in\n'
     32             'for = False\n'
     33             'clich = "this is a string with unicode, what a clich"'
     34             )
     35 
     36     @classmethod
     37     def setUpClass(cls):
     38         requires('gui')
     39         cls.root = Tk()
     40         cls.root.withdraw()
     41         cls.text = Text(cls.root)
     42         cls.editwin = DummyEditwin(cls.text)
     43 
     44     @classmethod
     45     def tearDownClass(cls):
     46         del cls.text, cls.editwin
     47         cls.root.destroy()
     48         del cls.root
     49 
     50     def setUp(self):
     51         self.text.insert('insert', self.code)
     52 
     53     def tearDown(self):
     54         self.text.delete('1.0', 'end')
     55         self.editwin.context_use_ps1 = True
     56 
     57     def get_parser(self, index):
     58         """
     59         Return a parser object with index at 'index'
     60         """
     61         return HyperParser(self.editwin, index)
     62 
     63     def test_init(self):
     64         """
     65         test corner cases in the init method
     66         """
     67         with self.assertRaises(ValueError) as ve:
     68             self.text.tag_add('console', '1.0', '1.end')
     69             p = self.get_parser('1.5')
     70         self.assertIn('precedes', str(ve.exception))
     71 
     72         # test without ps1
     73         self.editwin.context_use_ps1 = False
     74 
     75         # number of lines lesser than 50
     76         p = self.get_parser('end')
     77         self.assertEqual(p.rawtext, self.text.get('1.0', 'end'))
     78 
     79         # number of lines greater than 50
     80         self.text.insert('end', self.text.get('1.0', 'end')*4)
     81         p = self.get_parser('54.5')
     82 
     83     def test_is_in_string(self):
     84         get = self.get_parser
     85 
     86         p = get('1.0')
     87         self.assertFalse(p.is_in_string())
     88         p = get('1.4')
     89         self.assertTrue(p.is_in_string())
     90         p = get('2.3')
     91         self.assertFalse(p.is_in_string())
     92         p = get('3.3')
     93         self.assertFalse(p.is_in_string())
     94         p = get('3.7')
     95         self.assertTrue(p.is_in_string())
     96         p = get('4.6')
     97         self.assertTrue(p.is_in_string())
     98         p = get('12.54')
     99         self.assertTrue(p.is_in_string())
    100 
    101     def test_is_in_code(self):
    102         get = self.get_parser
    103 
    104         p = get('1.0')
    105         self.assertTrue(p.is_in_code())
    106         p = get('1.1')
    107         self.assertFalse(p.is_in_code())
    108         p = get('2.5')
    109         self.assertFalse(p.is_in_code())
    110         p = get('3.4')
    111         self.assertTrue(p.is_in_code())
    112         p = get('3.6')
    113         self.assertFalse(p.is_in_code())
    114         p = get('4.14')
    115         self.assertFalse(p.is_in_code())
    116 
    117     def test_get_surrounding_bracket(self):
    118         get = self.get_parser
    119 
    120         def without_mustclose(parser):
    121             # a utility function to get surrounding bracket
    122             # with mustclose=False
    123             return parser.get_surrounding_brackets(mustclose=False)
    124 
    125         def with_mustclose(parser):
    126             # a utility function to get surrounding bracket
    127             # with mustclose=True
    128             return parser.get_surrounding_brackets(mustclose=True)
    129 
    130         p = get('3.2')
    131         self.assertIsNone(with_mustclose(p))
    132         self.assertIsNone(without_mustclose(p))
    133 
    134         p = get('5.6')
    135         self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25'))
    136         self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
    137 
    138         p = get('5.23')
    139         self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24'))
    140         self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
    141 
    142         p = get('6.15')
    143         self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end'))
    144         self.assertIsNone(with_mustclose(p))
    145 
    146         p = get('9.end')
    147         self.assertIsNone(with_mustclose(p))
    148         self.assertIsNone(without_mustclose(p))
    149 
    150     def test_get_expression(self):
    151         get = self.get_parser
    152 
    153         p = get('4.2')
    154         self.assertEqual(p.get_expression(), 'y ')
    155 
    156         p = get('4.7')
    157         with self.assertRaises(ValueError) as ve:
    158             p.get_expression()
    159         self.assertIn('is inside a code', str(ve.exception))
    160 
    161         p = get('5.25')
    162         self.assertEqual(p.get_expression(), 'range(10)')
    163 
    164         p = get('6.7')
    165         self.assertEqual(p.get_expression(), 'py')
    166 
    167         p = get('6.8')
    168         self.assertEqual(p.get_expression(), '')
    169 
    170         p = get('7.9')
    171         self.assertEqual(p.get_expression(), 'py')
    172 
    173         p = get('8.end')
    174         self.assertEqual(p.get_expression(), 'x.__len__')
    175 
    176         p = get('9.13')
    177         self.assertEqual(p.get_expression(), "r'asdf'")
    178 
    179         p = get('9.17')
    180         with self.assertRaises(ValueError) as ve:
    181             p.get_expression()
    182         self.assertIn('is inside a code', str(ve.exception))
    183 
    184         p = get('10.0')
    185         self.assertEqual(p.get_expression(), '')
    186 
    187         p = get('10.6')
    188         self.assertEqual(p.get_expression(), '')
    189 
    190         p = get('10.11')
    191         self.assertEqual(p.get_expression(), '')
    192 
    193         p = get('11.3')
    194         self.assertEqual(p.get_expression(), '')
    195 
    196         p = get('11.11')
    197         self.assertEqual(p.get_expression(), 'False')
    198 
    199         p = get('12.6')
    200         self.assertEqual(p.get_expression(), 'clich')
    201 
    202     def test_eat_identifier(self):
    203         def is_valid_id(candidate):
    204             result = HyperParser._eat_identifier(candidate, 0, len(candidate))
    205             if result == len(candidate):
    206                 return True
    207             elif result == 0:
    208                 return False
    209             else:
    210                 err_msg = "Unexpected result: {} (expected 0 or {}".format(
    211                     result, len(candidate)
    212                 )
    213                 raise Exception(err_msg)
    214 
    215         # invalid first character which is valid elsewhere in an identifier
    216         self.assertFalse(is_valid_id('2notid'))
    217 
    218         # ASCII-only valid identifiers
    219         self.assertTrue(is_valid_id('valid_id'))
    220         self.assertTrue(is_valid_id('_valid_id'))
    221         self.assertTrue(is_valid_id('valid_id_'))
    222         self.assertTrue(is_valid_id('_2valid_id'))
    223 
    224         # keywords which should be "eaten"
    225         self.assertTrue(is_valid_id('True'))
    226         self.assertTrue(is_valid_id('False'))
    227         self.assertTrue(is_valid_id('None'))
    228 
    229         # keywords which should not be "eaten"
    230         self.assertFalse(is_valid_id('for'))
    231         self.assertFalse(is_valid_id('import'))
    232         self.assertFalse(is_valid_id('return'))
    233 
    234         # valid unicode identifiers
    235         self.assertTrue(is_valid_id('cliche'))
    236         self.assertTrue(is_valid_id('clich'))
    237         self.assertTrue(is_valid_id('a'))
    238 
    239         # invalid unicode identifiers
    240         self.assertFalse(is_valid_id('2a'))
    241         self.assertFalse(is_valid_id('a'))
    242         self.assertFalse(is_valid_id('a'))
    243 
    244         # valid identifier after "punctuation"
    245         self.assertEqual(HyperParser._eat_identifier('+ var', 0, 5), len('var'))
    246         self.assertEqual(HyperParser._eat_identifier('+var', 0, 4), len('var'))
    247         self.assertEqual(HyperParser._eat_identifier('.var', 0, 4), len('var'))
    248 
    249         # invalid identifiers
    250         self.assertFalse(is_valid_id('+'))
    251         self.assertFalse(is_valid_id(' '))
    252         self.assertFalse(is_valid_id(':'))
    253         self.assertFalse(is_valid_id('?'))
    254         self.assertFalse(is_valid_id('^'))
    255         self.assertFalse(is_valid_id('\\'))
    256         self.assertFalse(is_valid_id('"'))
    257         self.assertFalse(is_valid_id('"a string"'))
    258 
    259     def test_eat_identifier_various_lengths(self):
    260         eat_id = HyperParser._eat_identifier
    261 
    262         for length in range(1, 21):
    263             self.assertEqual(eat_id('a' * length, 0, length), length)
    264             self.assertEqual(eat_id('' * length, 0, length), length)
    265             self.assertEqual(eat_id('a' + '2' * (length - 1), 0, length), length)
    266             self.assertEqual(eat_id('' + '2' * (length - 1), 0, length), length)
    267             self.assertEqual(eat_id('' + 'a' * (length - 1), 0, length), length)
    268             self.assertEqual(eat_id('' * (length - 1) + 'a', 0, length), length)
    269             self.assertEqual(eat_id('+' * length, 0, length), 0)
    270             self.assertEqual(eat_id('2' + 'a' * (length - 1), 0, length), 0)
    271             self.assertEqual(eat_id('2' + '' * (length - 1), 0, length), 0)
    272 
    273 if __name__ == '__main__':
    274     unittest.main(verbosity=2)
    275