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 16gkd = config_key.GetKeysDialog 17 18 19class ValidationTest(unittest.TestCase): 20 "Test validation methods: ok, keys_ok, bind_ok." 21 22 class Validator(gkd): 23 def __init__(self, *args, **kwargs): 24 config_key.GetKeysDialog.__init__(self, *args, **kwargs) 25 class list_keys_final: 26 get = Func() 27 self.list_keys_final = list_keys_final 28 get_modifiers = Func() 29 showerror = Mbox_func() 30 31 @classmethod 32 def setUpClass(cls): 33 requires('gui') 34 cls.root = Tk() 35 cls.root.withdraw() 36 keylist = [['<Key-F12>'], ['<Control-Key-x>', '<Control-Key-X>']] 37 cls.dialog = cls.Validator( 38 cls.root, 'Title', '<<Test>>', keylist, _utest=True) 39 40 @classmethod 41 def tearDownClass(cls): 42 cls.dialog.cancel() 43 cls.root.update_idletasks() 44 cls.root.destroy() 45 del cls.dialog, cls.root 46 47 def setUp(self): 48 self.dialog.showerror.message = '' 49 # A test that needs a particular final key value should set it. 50 # A test that sets a non-blank modifier list should reset it to []. 51 52 def test_ok_empty(self): 53 self.dialog.key_string.set(' ') 54 self.dialog.ok() 55 self.assertEqual(self.dialog.result, '') 56 self.assertEqual(self.dialog.showerror.message, 'No key specified.') 57 58 def test_ok_good(self): 59 self.dialog.key_string.set('<Key-F11>') 60 self.dialog.list_keys_final.get.result = 'F11' 61 self.dialog.ok() 62 self.assertEqual(self.dialog.result, '<Key-F11>') 63 self.assertEqual(self.dialog.showerror.message, '') 64 65 def test_keys_no_ending(self): 66 self.assertFalse(self.dialog.keys_ok('<Control-Shift')) 67 self.assertIn('Missing the final', self.dialog.showerror.message) 68 69 def test_keys_no_modifier_bad(self): 70 self.dialog.list_keys_final.get.result = 'A' 71 self.assertFalse(self.dialog.keys_ok('<Key-A>')) 72 self.assertIn('No modifier', self.dialog.showerror.message) 73 74 def test_keys_no_modifier_ok(self): 75 self.dialog.list_keys_final.get.result = 'F11' 76 self.assertTrue(self.dialog.keys_ok('<Key-F11>')) 77 self.assertEqual(self.dialog.showerror.message, '') 78 79 def test_keys_shift_bad(self): 80 self.dialog.list_keys_final.get.result = 'a' 81 self.dialog.get_modifiers.result = ['Shift'] 82 self.assertFalse(self.dialog.keys_ok('<a>')) 83 self.assertIn('shift modifier', self.dialog.showerror.message) 84 self.dialog.get_modifiers.result = [] 85 86 def test_keys_dup(self): 87 for mods, final, seq in (([], 'F12', '<Key-F12>'), 88 (['Control'], 'x', '<Control-Key-x>'), 89 (['Control'], 'X', '<Control-Key-X>')): 90 with self.subTest(m=mods, f=final, s=seq): 91 self.dialog.list_keys_final.get.result = final 92 self.dialog.get_modifiers.result = mods 93 self.assertFalse(self.dialog.keys_ok(seq)) 94 self.assertIn('already in use', self.dialog.showerror.message) 95 self.dialog.get_modifiers.result = [] 96 97 def test_bind_ok(self): 98 self.assertTrue(self.dialog.bind_ok('<Control-Shift-Key-a>')) 99 self.assertEqual(self.dialog.showerror.message, '') 100 101 def test_bind_not_ok(self): 102 self.assertFalse(self.dialog.bind_ok('<Control-Shift>')) 103 self.assertIn('not accepted', self.dialog.showerror.message) 104 105 106class ToggleLevelTest(unittest.TestCase): 107 "Test toggle between Basic and Advanced frames." 108 109 @classmethod 110 def setUpClass(cls): 111 requires('gui') 112 cls.root = Tk() 113 cls.root.withdraw() 114 cls.dialog = gkd(cls.root, 'Title', '<<Test>>', [], _utest=True) 115 116 @classmethod 117 def tearDownClass(cls): 118 cls.dialog.cancel() 119 cls.root.update_idletasks() 120 cls.root.destroy() 121 del cls.dialog, cls.root 122 123 def test_toggle_level(self): 124 dialog = self.dialog 125 126 def stackorder(): 127 """Get the stack order of the children of the frame. 128 129 winfo_children() stores the children in stack order, so 130 this can be used to check whether a frame is above or 131 below another one. 132 """ 133 for index, child in enumerate(dialog.frame.winfo_children()): 134 if child._name == 'keyseq_basic': 135 basic = index 136 if child._name == 'keyseq_advanced': 137 advanced = index 138 return basic, advanced 139 140 # New window starts at basic level. 141 self.assertFalse(dialog.advanced) 142 self.assertIn('Advanced', dialog.button_level['text']) 143 basic, advanced = stackorder() 144 self.assertGreater(basic, advanced) 145 146 # Toggle to advanced. 147 dialog.toggle_level() 148 self.assertTrue(dialog.advanced) 149 self.assertIn('Basic', dialog.button_level['text']) 150 basic, advanced = stackorder() 151 self.assertGreater(advanced, basic) 152 153 # Toggle to basic. 154 dialog.button_level.invoke() 155 self.assertFalse(dialog.advanced) 156 self.assertIn('Advanced', dialog.button_level['text']) 157 basic, advanced = stackorder() 158 self.assertGreater(basic, advanced) 159 160 161class KeySelectionTest(unittest.TestCase): 162 "Test selecting key on Basic frames." 163 164 class Basic(gkd): 165 def __init__(self, *args, **kwargs): 166 super().__init__(*args, **kwargs) 167 class list_keys_final: 168 get = Func() 169 select_clear = Func() 170 yview = Func() 171 self.list_keys_final = list_keys_final 172 def set_modifiers_for_platform(self): 173 self.modifiers = ['foo', 'bar', 'BAZ'] 174 self.modifier_label = {'BAZ': 'ZZZ'} 175 showerror = Mbox_func() 176 177 @classmethod 178 def setUpClass(cls): 179 requires('gui') 180 cls.root = Tk() 181 cls.root.withdraw() 182 cls.dialog = cls.Basic(cls.root, 'Title', '<<Test>>', [], _utest=True) 183 184 @classmethod 185 def tearDownClass(cls): 186 cls.dialog.cancel() 187 cls.root.update_idletasks() 188 cls.root.destroy() 189 del cls.dialog, cls.root 190 191 def setUp(self): 192 self.dialog.clear_key_seq() 193 194 def test_get_modifiers(self): 195 dialog = self.dialog 196 gm = dialog.get_modifiers 197 eq = self.assertEqual 198 199 # Modifiers are set on/off by invoking the checkbutton. 200 dialog.modifier_checkbuttons['foo'].invoke() 201 eq(gm(), ['foo']) 202 203 dialog.modifier_checkbuttons['BAZ'].invoke() 204 eq(gm(), ['foo', 'BAZ']) 205 206 dialog.modifier_checkbuttons['foo'].invoke() 207 eq(gm(), ['BAZ']) 208 209 @mock.patch.object(gkd, 'get_modifiers') 210 def test_build_key_string(self, mock_modifiers): 211 dialog = self.dialog 212 key = dialog.list_keys_final 213 string = dialog.key_string.get 214 eq = self.assertEqual 215 216 key.get.result = 'a' 217 mock_modifiers.return_value = [] 218 dialog.build_key_string() 219 eq(string(), '<Key-a>') 220 221 mock_modifiers.return_value = ['mymod'] 222 dialog.build_key_string() 223 eq(string(), '<mymod-Key-a>') 224 225 key.get.result = '' 226 mock_modifiers.return_value = ['mymod', 'test'] 227 dialog.build_key_string() 228 eq(string(), '<mymod-test>') 229 230 @mock.patch.object(gkd, 'get_modifiers') 231 def test_final_key_selected(self, mock_modifiers): 232 dialog = self.dialog 233 key = dialog.list_keys_final 234 string = dialog.key_string.get 235 eq = self.assertEqual 236 237 mock_modifiers.return_value = ['Shift'] 238 key.get.result = '{' 239 dialog.final_key_selected() 240 eq(string(), '<Shift-Key-braceleft>') 241 242 243class CancelTest(unittest.TestCase): 244 "Simulate user clicking [Cancel] button." 245 246 @classmethod 247 def setUpClass(cls): 248 requires('gui') 249 cls.root = Tk() 250 cls.root.withdraw() 251 cls.dialog = gkd(cls.root, 'Title', '<<Test>>', [], _utest=True) 252 253 @classmethod 254 def tearDownClass(cls): 255 cls.dialog.cancel() 256 cls.root.update_idletasks() 257 cls.root.destroy() 258 del cls.dialog, cls.root 259 260 def test_cancel(self): 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 267 268class HelperTest(unittest.TestCase): 269 "Test module level helper functions." 270 271 def test_translate_key(self): 272 tr = config_key.translate_key 273 eq = self.assertEqual 274 275 # Letters return unchanged with no 'Shift'. 276 eq(tr('q', []), 'Key-q') 277 eq(tr('q', ['Control', 'Alt']), 'Key-q') 278 279 # 'Shift' uppercases single lowercase letters. 280 eq(tr('q', ['Shift']), 'Key-Q') 281 eq(tr('q', ['Control', 'Shift']), 'Key-Q') 282 eq(tr('q', ['Control', 'Alt', 'Shift']), 'Key-Q') 283 284 # Convert key name to keysym. 285 eq(tr('Page Up', []), 'Key-Prior') 286 # 'Shift' doesn't change case when it's not a single char. 287 eq(tr('*', ['Shift']), 'Key-asterisk') 288 289 290if __name__ == '__main__': 291 unittest.main(verbosity=2) 292