1"""Test config_key, coverage 98%. 2 3Coverage is effectively 100%. Tkinter dialog is mocked, Mac-only line 4may be skipped, and dummy function in bind test should not be called. 5Not tested: exit with 'self.advanced or self.keys_ok(keys) ...' False. 6""" 7 8from idlelib import config_key 9from test.support import requires 10import unittest 11from unittest import mock 12from tkinter import Tk, TclError 13from idlelib.idle_test.mock_idle import Func 14from idlelib.idle_test.mock_tk import Mbox_func 15 16 17class ValidationTest(unittest.TestCase): 18 "Test validation methods: ok, keys_ok, bind_ok." 19 20 class Validator(config_key.GetKeysFrame): 21 def __init__(self, *args, **kwargs): 22 super().__init__(*args, **kwargs) 23 class list_keys_final: 24 get = Func() 25 self.list_keys_final = list_keys_final 26 get_modifiers = Func() 27 showerror = Mbox_func() 28 29 @classmethod 30 def setUpClass(cls): 31 requires('gui') 32 cls.root = Tk() 33 cls.root.withdraw() 34 keylist = [['<Key-F12>'], ['<Control-Key-x>', '<Control-Key-X>']] 35 cls.dialog = cls.Validator(cls.root, '<<Test>>', keylist) 36 37 @classmethod 38 def tearDownClass(cls): 39 del cls.dialog 40 cls.root.update_idletasks() 41 cls.root.destroy() 42 del cls.root 43 44 def setUp(self): 45 self.dialog.showerror.message = '' 46 # A test that needs a particular final key value should set it. 47 # A test that sets a non-blank modifier list should reset it to []. 48 49 def test_ok_empty(self): 50 self.dialog.key_string.set(' ') 51 self.dialog.ok() 52 self.assertEqual(self.dialog.result, '') 53 self.assertEqual(self.dialog.showerror.message, 'No key specified.') 54 55 def test_ok_good(self): 56 self.dialog.key_string.set('<Key-F11>') 57 self.dialog.list_keys_final.get.result = 'F11' 58 self.dialog.ok() 59 self.assertEqual(self.dialog.result, '<Key-F11>') 60 self.assertEqual(self.dialog.showerror.message, '') 61 62 def test_keys_no_ending(self): 63 self.assertFalse(self.dialog.keys_ok('<Control-Shift')) 64 self.assertIn('Missing the final', self.dialog.showerror.message) 65 66 def test_keys_no_modifier_bad(self): 67 self.dialog.list_keys_final.get.result = 'A' 68 self.assertFalse(self.dialog.keys_ok('<Key-A>')) 69 self.assertIn('No modifier', self.dialog.showerror.message) 70 71 def test_keys_no_modifier_ok(self): 72 self.dialog.list_keys_final.get.result = 'F11' 73 self.assertTrue(self.dialog.keys_ok('<Key-F11>')) 74 self.assertEqual(self.dialog.showerror.message, '') 75 76 def test_keys_shift_bad(self): 77 self.dialog.list_keys_final.get.result = 'a' 78 self.dialog.get_modifiers.result = ['Shift'] 79 self.assertFalse(self.dialog.keys_ok('<a>')) 80 self.assertIn('shift modifier', self.dialog.showerror.message) 81 self.dialog.get_modifiers.result = [] 82 83 def test_keys_dup(self): 84 for mods, final, seq in (([], 'F12', '<Key-F12>'), 85 (['Control'], 'x', '<Control-Key-x>'), 86 (['Control'], 'X', '<Control-Key-X>')): 87 with self.subTest(m=mods, f=final, s=seq): 88 self.dialog.list_keys_final.get.result = final 89 self.dialog.get_modifiers.result = mods 90 self.assertFalse(self.dialog.keys_ok(seq)) 91 self.assertIn('already in use', self.dialog.showerror.message) 92 self.dialog.get_modifiers.result = [] 93 94 def test_bind_ok(self): 95 self.assertTrue(self.dialog.bind_ok('<Control-Shift-Key-a>')) 96 self.assertEqual(self.dialog.showerror.message, '') 97 98 def test_bind_not_ok(self): 99 self.assertFalse(self.dialog.bind_ok('<Control-Shift>')) 100 self.assertIn('not accepted', self.dialog.showerror.message) 101 102 103class ToggleLevelTest(unittest.TestCase): 104 "Test toggle between Basic and Advanced frames." 105 106 @classmethod 107 def setUpClass(cls): 108 requires('gui') 109 cls.root = Tk() 110 cls.root.withdraw() 111 cls.dialog = config_key.GetKeysFrame(cls.root, '<<Test>>', []) 112 113 @classmethod 114 def tearDownClass(cls): 115 del cls.dialog 116 cls.root.update_idletasks() 117 cls.root.destroy() 118 del cls.root 119 120 def test_toggle_level(self): 121 dialog = self.dialog 122 123 def stackorder(): 124 """Get the stack order of the children of the frame. 125 126 winfo_children() stores the children in stack order, so 127 this can be used to check whether a frame is above or 128 below another one. 129 """ 130 for index, child in enumerate(dialog.winfo_children()): 131 if child._name == 'keyseq_basic': 132 basic = index 133 if child._name == 'keyseq_advanced': 134 advanced = index 135 return basic, advanced 136 137 # New window starts at basic level. 138 self.assertFalse(dialog.advanced) 139 self.assertIn('Advanced', dialog.button_level['text']) 140 basic, advanced = stackorder() 141 self.assertGreater(basic, advanced) 142 143 # Toggle to advanced. 144 dialog.toggle_level() 145 self.assertTrue(dialog.advanced) 146 self.assertIn('Basic', dialog.button_level['text']) 147 basic, advanced = stackorder() 148 self.assertGreater(advanced, basic) 149 150 # Toggle to basic. 151 dialog.button_level.invoke() 152 self.assertFalse(dialog.advanced) 153 self.assertIn('Advanced', dialog.button_level['text']) 154 basic, advanced = stackorder() 155 self.assertGreater(basic, advanced) 156 157 158class KeySelectionTest(unittest.TestCase): 159 "Test selecting key on Basic frames." 160 161 class Basic(config_key.GetKeysFrame): 162 def __init__(self, *args, **kwargs): 163 super().__init__(*args, **kwargs) 164 class list_keys_final: 165 get = Func() 166 select_clear = Func() 167 yview = Func() 168 self.list_keys_final = list_keys_final 169 def set_modifiers_for_platform(self): 170 self.modifiers = ['foo', 'bar', 'BAZ'] 171 self.modifier_label = {'BAZ': 'ZZZ'} 172 showerror = Mbox_func() 173 174 @classmethod 175 def setUpClass(cls): 176 requires('gui') 177 cls.root = Tk() 178 cls.root.withdraw() 179 cls.dialog = cls.Basic(cls.root, '<<Test>>', []) 180 181 @classmethod 182 def tearDownClass(cls): 183 del cls.dialog 184 cls.root.update_idletasks() 185 cls.root.destroy() 186 del cls.root 187 188 def setUp(self): 189 self.dialog.clear_key_seq() 190 191 def test_get_modifiers(self): 192 dialog = self.dialog 193 gm = dialog.get_modifiers 194 eq = self.assertEqual 195 196 # Modifiers are set on/off by invoking the checkbutton. 197 dialog.modifier_checkbuttons['foo'].invoke() 198 eq(gm(), ['foo']) 199 200 dialog.modifier_checkbuttons['BAZ'].invoke() 201 eq(gm(), ['foo', 'BAZ']) 202 203 dialog.modifier_checkbuttons['foo'].invoke() 204 eq(gm(), ['BAZ']) 205 206 @mock.patch.object(config_key.GetKeysFrame, 'get_modifiers') 207 def test_build_key_string(self, mock_modifiers): 208 dialog = self.dialog 209 key = dialog.list_keys_final 210 string = dialog.key_string.get 211 eq = self.assertEqual 212 213 key.get.result = 'a' 214 mock_modifiers.return_value = [] 215 dialog.build_key_string() 216 eq(string(), '<Key-a>') 217 218 mock_modifiers.return_value = ['mymod'] 219 dialog.build_key_string() 220 eq(string(), '<mymod-Key-a>') 221 222 key.get.result = '' 223 mock_modifiers.return_value = ['mymod', 'test'] 224 dialog.build_key_string() 225 eq(string(), '<mymod-test>') 226 227 @mock.patch.object(config_key.GetKeysFrame, 'get_modifiers') 228 def test_final_key_selected(self, mock_modifiers): 229 dialog = self.dialog 230 key = dialog.list_keys_final 231 string = dialog.key_string.get 232 eq = self.assertEqual 233 234 mock_modifiers.return_value = ['Shift'] 235 key.get.result = '{' 236 dialog.final_key_selected() 237 eq(string(), '<Shift-Key-braceleft>') 238 239 240class CancelWindowTest(unittest.TestCase): 241 "Simulate user clicking [Cancel] button." 242 243 @classmethod 244 def setUpClass(cls): 245 requires('gui') 246 cls.root = Tk() 247 cls.root.withdraw() 248 cls.dialog = config_key.GetKeysWindow( 249 cls.root, 'Title', '<<Test>>', [], _utest=True) 250 251 @classmethod 252 def tearDownClass(cls): 253 cls.dialog.cancel() 254 del cls.dialog 255 cls.root.update_idletasks() 256 cls.root.destroy() 257 del cls.root 258 259 @mock.patch.object(config_key.GetKeysFrame, 'ok') 260 def test_cancel(self, mock_frame_ok): 261 self.assertEqual(self.dialog.winfo_class(), 'Toplevel') 262 self.dialog.button_cancel.invoke() 263 with self.assertRaises(TclError): 264 self.dialog.winfo_class() 265 self.assertEqual(self.dialog.result, '') 266 mock_frame_ok.assert_not_called() 267 268 269class OKWindowTest(unittest.TestCase): 270 "Simulate user clicking [OK] button." 271 272 @classmethod 273 def setUpClass(cls): 274 requires('gui') 275 cls.root = Tk() 276 cls.root.withdraw() 277 cls.dialog = config_key.GetKeysWindow( 278 cls.root, 'Title', '<<Test>>', [], _utest=True) 279 280 @classmethod 281 def tearDownClass(cls): 282 cls.dialog.cancel() 283 del cls.dialog 284 cls.root.update_idletasks() 285 cls.root.destroy() 286 del cls.root 287 288 @mock.patch.object(config_key.GetKeysFrame, 'ok') 289 def test_ok(self, mock_frame_ok): 290 self.assertEqual(self.dialog.winfo_class(), 'Toplevel') 291 self.dialog.button_ok.invoke() 292 with self.assertRaises(TclError): 293 self.dialog.winfo_class() 294 mock_frame_ok.assert_called() 295 296 297class WindowResultTest(unittest.TestCase): 298 "Test window result get and set." 299 300 @classmethod 301 def setUpClass(cls): 302 requires('gui') 303 cls.root = Tk() 304 cls.root.withdraw() 305 cls.dialog = config_key.GetKeysWindow( 306 cls.root, 'Title', '<<Test>>', [], _utest=True) 307 308 @classmethod 309 def tearDownClass(cls): 310 cls.dialog.cancel() 311 del cls.dialog 312 cls.root.update_idletasks() 313 cls.root.destroy() 314 del cls.root 315 316 def test_result(self): 317 dialog = self.dialog 318 eq = self.assertEqual 319 320 dialog.result = '' 321 eq(dialog.result, '') 322 eq(dialog.frame.result,'') 323 324 dialog.result = 'bar' 325 eq(dialog.result,'bar') 326 eq(dialog.frame.result,'bar') 327 328 dialog.frame.result = 'foo' 329 eq(dialog.result, 'foo') 330 eq(dialog.frame.result,'foo') 331 332 333class HelperTest(unittest.TestCase): 334 "Test module level helper functions." 335 336 def test_translate_key(self): 337 tr = config_key.translate_key 338 eq = self.assertEqual 339 340 # Letters return unchanged with no 'Shift'. 341 eq(tr('q', []), 'Key-q') 342 eq(tr('q', ['Control', 'Alt']), 'Key-q') 343 344 # 'Shift' uppercases single lowercase letters. 345 eq(tr('q', ['Shift']), 'Key-Q') 346 eq(tr('q', ['Control', 'Shift']), 'Key-Q') 347 eq(tr('q', ['Control', 'Alt', 'Shift']), 'Key-Q') 348 349 # Convert key name to keysym. 350 eq(tr('Page Up', []), 'Key-Prior') 351 # 'Shift' doesn't change case when it's not a single char. 352 eq(tr('*', ['Shift']), 'Key-asterisk') 353 354 355if __name__ == '__main__': 356 unittest.main(verbosity=2) 357