1"""Test query, coverage 93%. 2 3Non-gui tests for Query, SectionName, ModuleName, and HelpSource use 4dummy versions that extract the non-gui methods and add other needed 5attributes. GUI tests create an instance of each class and simulate 6entries and button clicks. Subclass tests only target the new code in 7the subclass definition. 8 9The appearance of the widgets is checked by the Query and 10HelpSource htests. These are run by running query.py. 11""" 12from idlelib import query 13import unittest 14from test.support import requires 15from tkinter import Tk, END 16 17import sys 18from unittest import mock 19from idlelib.idle_test.mock_tk import Var 20 21 22# NON-GUI TESTS 23 24class QueryTest(unittest.TestCase): 25 "Test Query base class." 26 27 class Dummy_Query: 28 # Test the following Query methods. 29 entry_ok = query.Query.entry_ok 30 ok = query.Query.ok 31 cancel = query.Query.cancel 32 # Add attributes and initialization needed for tests. 33 def __init__(self, dummy_entry): 34 self.entry = Var(value=dummy_entry) 35 self.entry_error = {'text': ''} 36 self.result = None 37 self.destroyed = False 38 def showerror(self, message): 39 self.entry_error['text'] = message 40 def destroy(self): 41 self.destroyed = True 42 43 def test_entry_ok_blank(self): 44 dialog = self.Dummy_Query(' ') 45 self.assertEqual(dialog.entry_ok(), None) 46 self.assertEqual((dialog.result, dialog.destroyed), (None, False)) 47 self.assertIn('blank line', dialog.entry_error['text']) 48 49 def test_entry_ok_good(self): 50 dialog = self.Dummy_Query(' good ') 51 Equal = self.assertEqual 52 Equal(dialog.entry_ok(), 'good') 53 Equal((dialog.result, dialog.destroyed), (None, False)) 54 Equal(dialog.entry_error['text'], '') 55 56 def test_ok_blank(self): 57 dialog = self.Dummy_Query('') 58 dialog.entry.focus_set = mock.Mock() 59 self.assertEqual(dialog.ok(), None) 60 self.assertTrue(dialog.entry.focus_set.called) 61 del dialog.entry.focus_set 62 self.assertEqual((dialog.result, dialog.destroyed), (None, False)) 63 64 def test_ok_good(self): 65 dialog = self.Dummy_Query('good') 66 self.assertEqual(dialog.ok(), None) 67 self.assertEqual((dialog.result, dialog.destroyed), ('good', True)) 68 69 def test_cancel(self): 70 dialog = self.Dummy_Query('does not matter') 71 self.assertEqual(dialog.cancel(), None) 72 self.assertEqual((dialog.result, dialog.destroyed), (None, True)) 73 74 75class SectionNameTest(unittest.TestCase): 76 "Test SectionName subclass of Query." 77 78 class Dummy_SectionName: 79 entry_ok = query.SectionName.entry_ok # Function being tested. 80 used_names = ['used'] 81 def __init__(self, dummy_entry): 82 self.entry = Var(value=dummy_entry) 83 self.entry_error = {'text': ''} 84 def showerror(self, message): 85 self.entry_error['text'] = message 86 87 def test_blank_section_name(self): 88 dialog = self.Dummy_SectionName(' ') 89 self.assertEqual(dialog.entry_ok(), None) 90 self.assertIn('no name', dialog.entry_error['text']) 91 92 def test_used_section_name(self): 93 dialog = self.Dummy_SectionName('used') 94 self.assertEqual(dialog.entry_ok(), None) 95 self.assertIn('use', dialog.entry_error['text']) 96 97 def test_long_section_name(self): 98 dialog = self.Dummy_SectionName('good'*8) 99 self.assertEqual(dialog.entry_ok(), None) 100 self.assertIn('longer than 30', dialog.entry_error['text']) 101 102 def test_good_section_name(self): 103 dialog = self.Dummy_SectionName(' good ') 104 self.assertEqual(dialog.entry_ok(), 'good') 105 self.assertEqual(dialog.entry_error['text'], '') 106 107 108class ModuleNameTest(unittest.TestCase): 109 "Test ModuleName subclass of Query." 110 111 class Dummy_ModuleName: 112 entry_ok = query.ModuleName.entry_ok # Function being tested. 113 text0 = '' 114 def __init__(self, dummy_entry): 115 self.entry = Var(value=dummy_entry) 116 self.entry_error = {'text': ''} 117 def showerror(self, message): 118 self.entry_error['text'] = message 119 120 def test_blank_module_name(self): 121 dialog = self.Dummy_ModuleName(' ') 122 self.assertEqual(dialog.entry_ok(), None) 123 self.assertIn('no name', dialog.entry_error['text']) 124 125 def test_bogus_module_name(self): 126 dialog = self.Dummy_ModuleName('__name_xyz123_should_not_exist__') 127 self.assertEqual(dialog.entry_ok(), None) 128 self.assertIn('not found', dialog.entry_error['text']) 129 130 def test_c_source_name(self): 131 dialog = self.Dummy_ModuleName('itertools') 132 self.assertEqual(dialog.entry_ok(), None) 133 self.assertIn('source-based', dialog.entry_error['text']) 134 135 def test_good_module_name(self): 136 dialog = self.Dummy_ModuleName('idlelib') 137 self.assertTrue(dialog.entry_ok().endswith('__init__.py')) 138 self.assertEqual(dialog.entry_error['text'], '') 139 dialog = self.Dummy_ModuleName('idlelib.idle') 140 self.assertTrue(dialog.entry_ok().endswith('idle.py')) 141 self.assertEqual(dialog.entry_error['text'], '') 142 143 144class GotoTest(unittest.TestCase): 145 "Test Goto subclass of Query." 146 147 class Dummy_ModuleName: 148 entry_ok = query.Goto.entry_ok # Function being tested. 149 def __init__(self, dummy_entry): 150 self.entry = Var(value=dummy_entry) 151 self.entry_error = {'text': ''} 152 def showerror(self, message): 153 self.entry_error['text'] = message 154 155 def test_bogus_goto(self): 156 dialog = self.Dummy_ModuleName('a') 157 self.assertEqual(dialog.entry_ok(), None) 158 self.assertIn('not a base 10 integer', dialog.entry_error['text']) 159 160 def test_bad_goto(self): 161 dialog = self.Dummy_ModuleName('0') 162 self.assertEqual(dialog.entry_ok(), None) 163 self.assertIn('not a positive integer', dialog.entry_error['text']) 164 165 def test_good_goto(self): 166 dialog = self.Dummy_ModuleName('1') 167 self.assertEqual(dialog.entry_ok(), 1) 168 self.assertEqual(dialog.entry_error['text'], '') 169 170 171# 3 HelpSource test classes each test one method. 172 173class HelpsourceBrowsefileTest(unittest.TestCase): 174 "Test browse_file method of ModuleName subclass of Query." 175 176 class Dummy_HelpSource: 177 browse_file = query.HelpSource.browse_file 178 pathvar = Var() 179 180 def test_file_replaces_path(self): 181 dialog = self.Dummy_HelpSource() 182 # Path is widget entry, either '' or something. 183 # Func return is file dialog return, either '' or something. 184 # Func return should override widget entry. 185 # We need all 4 combinations to test all (most) code paths. 186 for path, func, result in ( 187 ('', lambda a,b,c:'', ''), 188 ('', lambda a,b,c: __file__, __file__), 189 ('htest', lambda a,b,c:'', 'htest'), 190 ('htest', lambda a,b,c: __file__, __file__)): 191 with self.subTest(): 192 dialog.pathvar.set(path) 193 dialog.askfilename = func 194 dialog.browse_file() 195 self.assertEqual(dialog.pathvar.get(), result) 196 197 198class HelpsourcePathokTest(unittest.TestCase): 199 "Test path_ok method of HelpSource subclass of Query." 200 201 class Dummy_HelpSource: 202 path_ok = query.HelpSource.path_ok 203 def __init__(self, dummy_path): 204 self.path = Var(value=dummy_path) 205 self.path_error = {'text': ''} 206 def showerror(self, message, widget=None): 207 self.path_error['text'] = message 208 209 orig_platform = query.platform # Set in test_path_ok_file. 210 @classmethod 211 def tearDownClass(cls): 212 query.platform = cls.orig_platform 213 214 def test_path_ok_blank(self): 215 dialog = self.Dummy_HelpSource(' ') 216 self.assertEqual(dialog.path_ok(), None) 217 self.assertIn('no help file', dialog.path_error['text']) 218 219 def test_path_ok_bad(self): 220 dialog = self.Dummy_HelpSource(__file__ + 'bad-bad-bad') 221 self.assertEqual(dialog.path_ok(), None) 222 self.assertIn('not exist', dialog.path_error['text']) 223 224 def test_path_ok_web(self): 225 dialog = self.Dummy_HelpSource('') 226 Equal = self.assertEqual 227 for url in 'www.py.org', 'http://py.org': 228 with self.subTest(): 229 dialog.path.set(url) 230 self.assertEqual(dialog.path_ok(), url) 231 self.assertEqual(dialog.path_error['text'], '') 232 233 def test_path_ok_file(self): 234 dialog = self.Dummy_HelpSource('') 235 for platform, prefix in ('darwin', 'file://'), ('other', ''): 236 with self.subTest(): 237 query.platform = platform 238 dialog.path.set(__file__) 239 self.assertEqual(dialog.path_ok(), prefix + __file__) 240 self.assertEqual(dialog.path_error['text'], '') 241 242 243class HelpsourceEntryokTest(unittest.TestCase): 244 "Test entry_ok method of HelpSource subclass of Query." 245 246 class Dummy_HelpSource: 247 entry_ok = query.HelpSource.entry_ok 248 entry_error = {} 249 path_error = {} 250 def item_ok(self): 251 return self.name 252 def path_ok(self): 253 return self.path 254 255 def test_entry_ok_helpsource(self): 256 dialog = self.Dummy_HelpSource() 257 for name, path, result in ((None, None, None), 258 (None, 'doc.txt', None), 259 ('doc', None, None), 260 ('doc', 'doc.txt', ('doc', 'doc.txt'))): 261 with self.subTest(): 262 dialog.name, dialog.path = name, path 263 self.assertEqual(dialog.entry_ok(), result) 264 265 266# 2 CustomRun test classes each test one method. 267 268class CustomRunCLIargsokTest(unittest.TestCase): 269 "Test cli_ok method of the CustomRun subclass of Query." 270 271 class Dummy_CustomRun: 272 cli_args_ok = query.CustomRun.cli_args_ok 273 def __init__(self, dummy_entry): 274 self.entry = Var(value=dummy_entry) 275 self.entry_error = {'text': ''} 276 def showerror(self, message): 277 self.entry_error['text'] = message 278 279 def test_blank_args(self): 280 dialog = self.Dummy_CustomRun(' ') 281 self.assertEqual(dialog.cli_args_ok(), []) 282 283 def test_invalid_args(self): 284 dialog = self.Dummy_CustomRun("'no-closing-quote") 285 self.assertEqual(dialog.cli_args_ok(), None) 286 self.assertIn('No closing', dialog.entry_error['text']) 287 288 def test_good_args(self): 289 args = ['-n', '10', '--verbose', '-p', '/path', '--name'] 290 dialog = self.Dummy_CustomRun(' '.join(args) + ' "my name"') 291 self.assertEqual(dialog.cli_args_ok(), args + ["my name"]) 292 self.assertEqual(dialog.entry_error['text'], '') 293 294 295class CustomRunEntryokTest(unittest.TestCase): 296 "Test entry_ok method of the CustomRun subclass of Query." 297 298 class Dummy_CustomRun: 299 entry_ok = query.CustomRun.entry_ok 300 entry_error = {} 301 restartvar = Var() 302 def cli_args_ok(self): 303 return self.cli_args 304 305 def test_entry_ok_customrun(self): 306 dialog = self.Dummy_CustomRun() 307 for restart in {True, False}: 308 dialog.restartvar.set(restart) 309 for cli_args, result in ((None, None), 310 (['my arg'], (['my arg'], restart))): 311 with self.subTest(restart=restart, cli_args=cli_args): 312 dialog.cli_args = cli_args 313 self.assertEqual(dialog.entry_ok(), result) 314 315 316# GUI TESTS 317 318class QueryGuiTest(unittest.TestCase): 319 320 @classmethod 321 def setUpClass(cls): 322 requires('gui') 323 cls.root = root = Tk() 324 cls.root.withdraw() 325 cls.dialog = query.Query(root, 'TEST', 'test', _utest=True) 326 cls.dialog.destroy = mock.Mock() 327 328 @classmethod 329 def tearDownClass(cls): 330 del cls.dialog.destroy 331 del cls.dialog 332 cls.root.destroy() 333 del cls.root 334 335 def setUp(self): 336 self.dialog.entry.delete(0, 'end') 337 self.dialog.result = None 338 self.dialog.destroy.reset_mock() 339 340 def test_click_ok(self): 341 dialog = self.dialog 342 dialog.entry.insert(0, 'abc') 343 dialog.button_ok.invoke() 344 self.assertEqual(dialog.result, 'abc') 345 self.assertTrue(dialog.destroy.called) 346 347 def test_click_blank(self): 348 dialog = self.dialog 349 dialog.button_ok.invoke() 350 self.assertEqual(dialog.result, None) 351 self.assertFalse(dialog.destroy.called) 352 353 def test_click_cancel(self): 354 dialog = self.dialog 355 dialog.entry.insert(0, 'abc') 356 dialog.button_cancel.invoke() 357 self.assertEqual(dialog.result, None) 358 self.assertTrue(dialog.destroy.called) 359 360 361class SectionnameGuiTest(unittest.TestCase): 362 363 @classmethod 364 def setUpClass(cls): 365 requires('gui') 366 367 def test_click_section_name(self): 368 root = Tk() 369 root.withdraw() 370 dialog = query.SectionName(root, 'T', 't', {'abc'}, _utest=True) 371 Equal = self.assertEqual 372 self.assertEqual(dialog.used_names, {'abc'}) 373 dialog.entry.insert(0, 'okay') 374 dialog.button_ok.invoke() 375 self.assertEqual(dialog.result, 'okay') 376 root.destroy() 377 378 379class ModulenameGuiTest(unittest.TestCase): 380 381 @classmethod 382 def setUpClass(cls): 383 requires('gui') 384 385 def test_click_module_name(self): 386 root = Tk() 387 root.withdraw() 388 dialog = query.ModuleName(root, 'T', 't', 'idlelib', _utest=True) 389 self.assertEqual(dialog.text0, 'idlelib') 390 self.assertEqual(dialog.entry.get(), 'idlelib') 391 dialog.button_ok.invoke() 392 self.assertTrue(dialog.result.endswith('__init__.py')) 393 root.destroy() 394 395 396class GotoGuiTest(unittest.TestCase): 397 398 @classmethod 399 def setUpClass(cls): 400 requires('gui') 401 402 def test_click_module_name(self): 403 root = Tk() 404 root.withdraw() 405 dialog = query.Goto(root, 'T', 't', _utest=True) 406 dialog.entry.insert(0, '22') 407 dialog.button_ok.invoke() 408 self.assertEqual(dialog.result, 22) 409 root.destroy() 410 411 412class HelpsourceGuiTest(unittest.TestCase): 413 414 @classmethod 415 def setUpClass(cls): 416 requires('gui') 417 418 def test_click_help_source(self): 419 root = Tk() 420 root.withdraw() 421 dialog = query.HelpSource(root, 'T', menuitem='__test__', 422 filepath=__file__, _utest=True) 423 Equal = self.assertEqual 424 Equal(dialog.entry.get(), '__test__') 425 Equal(dialog.path.get(), __file__) 426 dialog.button_ok.invoke() 427 prefix = "file://" if sys.platform == 'darwin' else '' 428 Equal(dialog.result, ('__test__', prefix + __file__)) 429 root.destroy() 430 431 432class CustomRunGuiTest(unittest.TestCase): 433 434 @classmethod 435 def setUpClass(cls): 436 requires('gui') 437 438 def test_click_args(self): 439 root = Tk() 440 root.withdraw() 441 dialog = query.CustomRun(root, 'Title', 442 cli_args=['a', 'b=1'], _utest=True) 443 self.assertEqual(dialog.entry.get(), 'a b=1') 444 dialog.entry.insert(END, ' c') 445 dialog.button_ok.invoke() 446 self.assertEqual(dialog.result, (['a', 'b=1', 'c'], True)) 447 root.destroy() 448 449 450if __name__ == '__main__': 451 unittest.main(verbosity=2, exit=False) 452