1"Test editor, coverage 35%." 2 3from idlelib import editor 4import unittest 5from collections import namedtuple 6from test.support import requires 7from tkinter import Tk 8from idlelib.idle_test.mock_idle import Func 9 10Editor = editor.EditorWindow 11 12 13class EditorWindowTest(unittest.TestCase): 14 15 @classmethod 16 def setUpClass(cls): 17 requires('gui') 18 cls.root = Tk() 19 cls.root.withdraw() 20 21 @classmethod 22 def tearDownClass(cls): 23 cls.root.update_idletasks() 24 for id in cls.root.tk.call('after', 'info'): 25 cls.root.after_cancel(id) 26 cls.root.destroy() 27 del cls.root 28 29 def test_init(self): 30 e = Editor(root=self.root) 31 self.assertEqual(e.root, self.root) 32 e._close() 33 34 35class TestGetLineIndent(unittest.TestCase): 36 def test_empty_lines(self): 37 for tabwidth in [1, 2, 4, 6, 8]: 38 for line in ['', '\n']: 39 with self.subTest(line=line, tabwidth=tabwidth): 40 self.assertEqual( 41 editor.get_line_indent(line, tabwidth=tabwidth), 42 (0, 0), 43 ) 44 45 def test_tabwidth_4(self): 46 # (line, (raw, effective)) 47 tests = (('no spaces', (0, 0)), 48 # Internal space isn't counted. 49 (' space test', (4, 4)), 50 ('\ttab test', (1, 4)), 51 ('\t\tdouble tabs test', (2, 8)), 52 # Different results when mixing tabs and spaces. 53 (' \tmixed test', (5, 8)), 54 (' \t mixed test', (5, 6)), 55 ('\t mixed test', (5, 8)), 56 # Spaces not divisible by tabwidth. 57 (' \tmixed test', (3, 4)), 58 (' \t mixed test', (3, 5)), 59 ('\t mixed test', (3, 6)), 60 # Only checks spaces and tabs. 61 ('\nnewline test', (0, 0))) 62 63 for line, expected in tests: 64 with self.subTest(line=line): 65 self.assertEqual( 66 editor.get_line_indent(line, tabwidth=4), 67 expected, 68 ) 69 70 def test_tabwidth_8(self): 71 # (line, (raw, effective)) 72 tests = (('no spaces', (0, 0)), 73 # Internal space isn't counted. 74 (' space test', (8, 8)), 75 ('\ttab test', (1, 8)), 76 ('\t\tdouble tabs test', (2, 16)), 77 # Different results when mixing tabs and spaces. 78 (' \tmixed test', (9, 16)), 79 (' \t mixed test', (9, 10)), 80 ('\t mixed test', (9, 16)), 81 # Spaces not divisible by tabwidth. 82 (' \tmixed test', (3, 8)), 83 (' \t mixed test', (3, 9)), 84 ('\t mixed test', (3, 10)), 85 # Only checks spaces and tabs. 86 ('\nnewline test', (0, 0))) 87 88 for line, expected in tests: 89 with self.subTest(line=line): 90 self.assertEqual( 91 editor.get_line_indent(line, tabwidth=8), 92 expected, 93 ) 94 95 96def insert(text, string): 97 text.delete('1.0', 'end') 98 text.insert('end', string) 99 text.update() # Force update for colorizer to finish. 100 101 102class IndentAndNewlineTest(unittest.TestCase): 103 104 @classmethod 105 def setUpClass(cls): 106 requires('gui') 107 cls.root = Tk() 108 cls.root.withdraw() 109 cls.window = Editor(root=cls.root) 110 cls.window.indentwidth = 2 111 cls.window.tabwidth = 2 112 113 @classmethod 114 def tearDownClass(cls): 115 cls.window._close() 116 del cls.window 117 cls.root.update_idletasks() 118 for id in cls.root.tk.call('after', 'info'): 119 cls.root.after_cancel(id) 120 cls.root.destroy() 121 del cls.root 122 123 def test_indent_and_newline_event(self): 124 eq = self.assertEqual 125 w = self.window 126 text = w.text 127 get = text.get 128 nl = w.newline_and_indent_event 129 130 TestInfo = namedtuple('Tests', ['label', 'text', 'expected', 'mark']) 131 132 tests = (TestInfo('Empty line inserts with no indent.', 133 ' \n def __init__(self):', 134 '\n \n def __init__(self):\n', 135 '1.end'), 136 TestInfo('Inside bracket before space, deletes space.', 137 ' def f1(self, a, b):', 138 ' def f1(self,\n a, b):\n', 139 '1.14'), 140 TestInfo('Inside bracket after space, deletes space.', 141 ' def f1(self, a, b):', 142 ' def f1(self,\n a, b):\n', 143 '1.15'), 144 TestInfo('Inside string with one line - no indent.', 145 ' """Docstring."""', 146 ' """Docstring.\n"""\n', 147 '1.15'), 148 TestInfo('Inside string with more than one line.', 149 ' """Docstring.\n Docstring Line 2"""', 150 ' """Docstring.\n Docstring Line 2\n """\n', 151 '2.18'), 152 TestInfo('Backslash with one line.', 153 'a =\\', 154 'a =\\\n \n', 155 '1.end'), 156 TestInfo('Backslash with more than one line.', 157 'a =\\\n multiline\\', 158 'a =\\\n multiline\\\n \n', 159 '2.end'), 160 TestInfo('Block opener - indents +1 level.', 161 ' def f1(self):\n pass', 162 ' def f1(self):\n \n pass\n', 163 '1.end'), 164 TestInfo('Block closer - dedents -1 level.', 165 ' def f1(self):\n pass', 166 ' def f1(self):\n pass\n \n', 167 '2.end'), 168 ) 169 170 w.prompt_last_line = '' 171 for test in tests: 172 with self.subTest(label=test.label): 173 insert(text, test.text) 174 text.mark_set('insert', test.mark) 175 nl(event=None) 176 eq(get('1.0', 'end'), test.expected) 177 178 # Selected text. 179 insert(text, ' def f1(self, a, b):\n return a + b') 180 text.tag_add('sel', '1.17', '1.end') 181 nl(None) 182 # Deletes selected text before adding new line. 183 eq(get('1.0', 'end'), ' def f1(self, a,\n \n return a + b\n') 184 185 # Preserves the whitespace in shell prompt. 186 w.prompt_last_line = '>>> ' 187 insert(text, '>>> \t\ta =') 188 text.mark_set('insert', '1.5') 189 nl(None) 190 eq(get('1.0', 'end'), '>>> \na =\n') 191 192 193class RMenuTest(unittest.TestCase): 194 195 @classmethod 196 def setUpClass(cls): 197 requires('gui') 198 cls.root = Tk() 199 cls.root.withdraw() 200 cls.window = Editor(root=cls.root) 201 202 @classmethod 203 def tearDownClass(cls): 204 cls.window._close() 205 del cls.window 206 cls.root.update_idletasks() 207 for id in cls.root.tk.call('after', 'info'): 208 cls.root.after_cancel(id) 209 cls.root.destroy() 210 del cls.root 211 212 class DummyRMenu: 213 def tk_popup(x, y): pass 214 215 def test_rclick(self): 216 pass 217 218 219if __name__ == '__main__': 220 unittest.main(verbosity=2) 221