1# Test the functions and main class method of paragraph.py 2import unittest 3from idlelib import paragraph as fp 4from idlelib.editor import EditorWindow 5from tkinter import Tk, Text 6from test.support import requires 7 8 9class Is_Get_Test(unittest.TestCase): 10 """Test the is_ and get_ functions""" 11 test_comment = '# This is a comment' 12 test_nocomment = 'This is not a comment' 13 trailingws_comment = '# This is a comment ' 14 leadingws_comment = ' # This is a comment' 15 leadingws_nocomment = ' This is not a comment' 16 17 def test_is_all_white(self): 18 self.assertTrue(fp.is_all_white('')) 19 self.assertTrue(fp.is_all_white('\t\n\r\f\v')) 20 self.assertFalse(fp.is_all_white(self.test_comment)) 21 22 def test_get_indent(self): 23 Equal = self.assertEqual 24 Equal(fp.get_indent(self.test_comment), '') 25 Equal(fp.get_indent(self.trailingws_comment), '') 26 Equal(fp.get_indent(self.leadingws_comment), ' ') 27 Equal(fp.get_indent(self.leadingws_nocomment), ' ') 28 29 def test_get_comment_header(self): 30 Equal = self.assertEqual 31 # Test comment strings 32 Equal(fp.get_comment_header(self.test_comment), '#') 33 Equal(fp.get_comment_header(self.trailingws_comment), '#') 34 Equal(fp.get_comment_header(self.leadingws_comment), ' #') 35 # Test non-comment strings 36 Equal(fp.get_comment_header(self.leadingws_nocomment), ' ') 37 Equal(fp.get_comment_header(self.test_nocomment), '') 38 39 40class FindTest(unittest.TestCase): 41 """Test the find_paragraph function in paragraph module. 42 43 Using the runcase() function, find_paragraph() is called with 'mark' set at 44 multiple indexes before and inside the test paragraph. 45 46 It appears that code with the same indentation as a quoted string is grouped 47 as part of the same paragraph, which is probably incorrect behavior. 48 """ 49 50 @classmethod 51 def setUpClass(cls): 52 from idlelib.idle_test.mock_tk import Text 53 cls.text = Text() 54 55 def runcase(self, inserttext, stopline, expected): 56 # Check that find_paragraph returns the expected paragraph when 57 # the mark index is set to beginning, middle, end of each line 58 # up to but not including the stop line 59 text = self.text 60 text.insert('1.0', inserttext) 61 for line in range(1, stopline): 62 linelength = int(text.index("%d.end" % line).split('.')[1]) 63 for col in (0, linelength//2, linelength): 64 tempindex = "%d.%d" % (line, col) 65 self.assertEqual(fp.find_paragraph(text, tempindex), expected) 66 text.delete('1.0', 'end') 67 68 def test_find_comment(self): 69 comment = ( 70 "# Comment block with no blank lines before\n" 71 "# Comment line\n" 72 "\n") 73 self.runcase(comment, 3, ('1.0', '3.0', '#', comment[0:58])) 74 75 comment = ( 76 "\n" 77 "# Comment block with whitespace line before and after\n" 78 "# Comment line\n" 79 "\n") 80 self.runcase(comment, 4, ('2.0', '4.0', '#', comment[1:70])) 81 82 comment = ( 83 "\n" 84 " # Indented comment block with whitespace before and after\n" 85 " # Comment line\n" 86 "\n") 87 self.runcase(comment, 4, ('2.0', '4.0', ' #', comment[1:82])) 88 89 comment = ( 90 "\n" 91 "# Single line comment\n" 92 "\n") 93 self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:23])) 94 95 comment = ( 96 "\n" 97 " # Single line comment with leading whitespace\n" 98 "\n") 99 self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:51])) 100 101 comment = ( 102 "\n" 103 "# Comment immediately followed by code\n" 104 "x = 42\n" 105 "\n") 106 self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:40])) 107 108 comment = ( 109 "\n" 110 " # Indented comment immediately followed by code\n" 111 "x = 42\n" 112 "\n") 113 self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:53])) 114 115 comment = ( 116 "\n" 117 "# Comment immediately followed by indented code\n" 118 " x = 42\n" 119 "\n") 120 self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:49])) 121 122 def test_find_paragraph(self): 123 teststring = ( 124 '"""String with no blank lines before\n' 125 'String line\n' 126 '"""\n' 127 '\n') 128 self.runcase(teststring, 4, ('1.0', '4.0', '', teststring[0:53])) 129 130 teststring = ( 131 "\n" 132 '"""String with whitespace line before and after\n' 133 'String line.\n' 134 '"""\n' 135 '\n') 136 self.runcase(teststring, 5, ('2.0', '5.0', '', teststring[1:66])) 137 138 teststring = ( 139 '\n' 140 ' """Indented string with whitespace before and after\n' 141 ' Comment string.\n' 142 ' """\n' 143 '\n') 144 self.runcase(teststring, 5, ('2.0', '5.0', ' ', teststring[1:85])) 145 146 teststring = ( 147 '\n' 148 '"""Single line string."""\n' 149 '\n') 150 self.runcase(teststring, 3, ('2.0', '3.0', '', teststring[1:27])) 151 152 teststring = ( 153 '\n' 154 ' """Single line string with leading whitespace."""\n' 155 '\n') 156 self.runcase(teststring, 3, ('2.0', '3.0', ' ', teststring[1:55])) 157 158 159class ReformatFunctionTest(unittest.TestCase): 160 """Test the reformat_paragraph function without the editor window.""" 161 162 def test_reformat_paragraph(self): 163 Equal = self.assertEqual 164 reform = fp.reformat_paragraph 165 hw = "O hello world" 166 Equal(reform(' ', 1), ' ') 167 Equal(reform("Hello world", 20), "Hello world") 168 169 # Test without leading newline 170 Equal(reform(hw, 1), "O\nhello\nworld") 171 Equal(reform(hw, 6), "O\nhello\nworld") 172 Equal(reform(hw, 7), "O hello\nworld") 173 Equal(reform(hw, 12), "O hello\nworld") 174 Equal(reform(hw, 13), "O hello world") 175 176 # Test with leading newline 177 hw = "\nO hello world" 178 Equal(reform(hw, 1), "\nO\nhello\nworld") 179 Equal(reform(hw, 6), "\nO\nhello\nworld") 180 Equal(reform(hw, 7), "\nO hello\nworld") 181 Equal(reform(hw, 12), "\nO hello\nworld") 182 Equal(reform(hw, 13), "\nO hello world") 183 184 185class ReformatCommentTest(unittest.TestCase): 186 """Test the reformat_comment function without the editor window.""" 187 188 def test_reformat_comment(self): 189 Equal = self.assertEqual 190 191 # reformat_comment formats to a minimum of 20 characters 192 test_string = ( 193 " \"\"\"this is a test of a reformat for a triple quoted string" 194 " will it reformat to less than 70 characters for me?\"\"\"") 195 result = fp.reformat_comment(test_string, 70, " ") 196 expected = ( 197 " \"\"\"this is a test of a reformat for a triple quoted string will it\n" 198 " reformat to less than 70 characters for me?\"\"\"") 199 Equal(result, expected) 200 201 test_comment = ( 202 "# this is a test of a reformat for a triple quoted string will " 203 "it reformat to less than 70 characters for me?") 204 result = fp.reformat_comment(test_comment, 70, "#") 205 expected = ( 206 "# this is a test of a reformat for a triple quoted string will it\n" 207 "# reformat to less than 70 characters for me?") 208 Equal(result, expected) 209 210 211class FormatClassTest(unittest.TestCase): 212 def test_init_close(self): 213 instance = fp.FormatParagraph('editor') 214 self.assertEqual(instance.editwin, 'editor') 215 instance.close() 216 self.assertEqual(instance.editwin, None) 217 218 219# For testing format_paragraph_event, Initialize FormatParagraph with 220# a mock Editor with .text and .get_selection_indices. The text must 221# be a Text wrapper that adds two methods 222 223# A real EditorWindow creates unneeded, time-consuming baggage and 224# sometimes emits shutdown warnings like this: 225# "warning: callback failed in WindowList <class '_tkinter.TclError'> 226# : invalid command name ".55131368.windows". 227# Calling EditorWindow._close in tearDownClass prevents this but causes 228# other problems (windows left open). 229 230class TextWrapper: 231 def __init__(self, master): 232 self.text = Text(master=master) 233 def __getattr__(self, name): 234 return getattr(self.text, name) 235 def undo_block_start(self): pass 236 def undo_block_stop(self): pass 237 238class Editor: 239 def __init__(self, root): 240 self.text = TextWrapper(root) 241 get_selection_indices = EditorWindow. get_selection_indices 242 243class FormatEventTest(unittest.TestCase): 244 """Test the formatting of text inside a Text widget. 245 246 This is done with FormatParagraph.format.paragraph_event, 247 which calls functions in the module as appropriate. 248 """ 249 test_string = ( 250 " '''this is a test of a reformat for a triple " 251 "quoted string will it reformat to less than 70 " 252 "characters for me?'''\n") 253 multiline_test_string = ( 254 " '''The first line is under the max width.\n" 255 " The second line's length is way over the max width. It goes " 256 "on and on until it is over 100 characters long.\n" 257 " Same thing with the third line. It is also way over the max " 258 "width, but FormatParagraph will fix it.\n" 259 " '''\n") 260 multiline_test_comment = ( 261 "# The first line is under the max width.\n" 262 "# The second line's length is way over the max width. It goes on " 263 "and on until it is over 100 characters long.\n" 264 "# Same thing with the third line. It is also way over the max " 265 "width, but FormatParagraph will fix it.\n" 266 "# The fourth line is short like the first line.") 267 268 @classmethod 269 def setUpClass(cls): 270 requires('gui') 271 cls.root = Tk() 272 editor = Editor(root=cls.root) 273 cls.text = editor.text.text # Test code does not need the wrapper. 274 cls.formatter = fp.FormatParagraph(editor).format_paragraph_event 275 # Sets the insert mark just after the re-wrapped and inserted text. 276 277 @classmethod 278 def tearDownClass(cls): 279 del cls.text, cls.formatter 280 cls.root.destroy() 281 del cls.root 282 283 def test_short_line(self): 284 self.text.insert('1.0', "Short line\n") 285 self.formatter("Dummy") 286 self.assertEqual(self.text.get('1.0', 'insert'), "Short line\n" ) 287 self.text.delete('1.0', 'end') 288 289 def test_long_line(self): 290 text = self.text 291 292 # Set cursor ('insert' mark) to '1.0', within text. 293 text.insert('1.0', self.test_string) 294 text.mark_set('insert', '1.0') 295 self.formatter('ParameterDoesNothing', limit=70) 296 result = text.get('1.0', 'insert') 297 # find function includes \n 298 expected = ( 299" '''this is a test of a reformat for a triple quoted string will it\n" 300" reformat to less than 70 characters for me?'''\n") # yes 301 self.assertEqual(result, expected) 302 text.delete('1.0', 'end') 303 304 # Select from 1.11 to line end. 305 text.insert('1.0', self.test_string) 306 text.tag_add('sel', '1.11', '1.end') 307 self.formatter('ParameterDoesNothing', limit=70) 308 result = text.get('1.0', 'insert') 309 # selection excludes \n 310 expected = ( 311" '''this is a test of a reformat for a triple quoted string will it reformat\n" 312" to less than 70 characters for me?'''") # no 313 self.assertEqual(result, expected) 314 text.delete('1.0', 'end') 315 316 def test_multiple_lines(self): 317 text = self.text 318 # Select 2 long lines. 319 text.insert('1.0', self.multiline_test_string) 320 text.tag_add('sel', '2.0', '4.0') 321 self.formatter('ParameterDoesNothing', limit=70) 322 result = text.get('2.0', 'insert') 323 expected = ( 324" The second line's length is way over the max width. It goes on and\n" 325" on until it is over 100 characters long. Same thing with the third\n" 326" line. It is also way over the max width, but FormatParagraph will\n" 327" fix it.\n") 328 self.assertEqual(result, expected) 329 text.delete('1.0', 'end') 330 331 def test_comment_block(self): 332 text = self.text 333 334 # Set cursor ('insert') to '1.0', within block. 335 text.insert('1.0', self.multiline_test_comment) 336 self.formatter('ParameterDoesNothing', limit=70) 337 result = text.get('1.0', 'insert') 338 expected = ( 339"# The first line is under the max width. The second line's length is\n" 340"# way over the max width. It goes on and on until it is over 100\n" 341"# characters long. Same thing with the third line. It is also way over\n" 342"# the max width, but FormatParagraph will fix it. The fourth line is\n" 343"# short like the first line.\n") 344 self.assertEqual(result, expected) 345 text.delete('1.0', 'end') 346 347 # Select line 2, verify line 1 unaffected. 348 text.insert('1.0', self.multiline_test_comment) 349 text.tag_add('sel', '2.0', '3.0') 350 self.formatter('ParameterDoesNothing', limit=70) 351 result = text.get('1.0', 'insert') 352 expected = ( 353"# The first line is under the max width.\n" 354"# The second line's length is way over the max width. It goes on and\n" 355"# on until it is over 100 characters long.\n") 356 self.assertEqual(result, expected) 357 text.delete('1.0', 'end') 358 359# The following block worked with EditorWindow but fails with the mock. 360# Lines 2 and 3 get pasted together even though the previous block left 361# the previous line alone. More investigation is needed. 362## # Select lines 3 and 4 363## text.insert('1.0', self.multiline_test_comment) 364## text.tag_add('sel', '3.0', '5.0') 365## self.formatter('ParameterDoesNothing') 366## result = text.get('3.0', 'insert') 367## expected = ( 368##"# Same thing with the third line. It is also way over the max width,\n" 369##"# but FormatParagraph will fix it. The fourth line is short like the\n" 370##"# first line.\n") 371## self.assertEqual(result, expected) 372## text.delete('1.0', 'end') 373 374 375if __name__ == '__main__': 376 unittest.main(verbosity=2, exit=2) 377