1from test.support import import_helper 2 3# Skip this test if _tkinter does not exist. 4import_helper.import_module('_tkinter') 5 6import unittest 7from tkinter import ttk 8 9 10class MockTkApp: 11 12 def splitlist(self, arg): 13 if isinstance(arg, tuple): 14 return arg 15 return arg.split(':') 16 17 def wantobjects(self): 18 return True 19 20 21class MockTclObj(object): 22 typename = 'test' 23 24 def __init__(self, val): 25 self.val = val 26 27 def __str__(self): 28 return str(self.val) 29 30 31class MockStateSpec(object): 32 typename = 'StateSpec' 33 34 def __init__(self, *args): 35 self.val = args 36 37 def __str__(self): 38 return ' '.join(self.val) 39 40 41class InternalFunctionsTest(unittest.TestCase): 42 43 def test_format_optdict(self): 44 def check_against(fmt_opts, result): 45 for i in range(0, len(fmt_opts), 2): 46 self.assertEqual(result.pop(fmt_opts[i]), fmt_opts[i + 1]) 47 if result: 48 self.fail("result still got elements: %s" % result) 49 50 # passing an empty dict should return an empty object (tuple here) 51 self.assertFalse(ttk._format_optdict({})) 52 53 # check list formatting 54 check_against( 55 ttk._format_optdict({'fg': 'blue', 'padding': [1, 2, 3, 4]}), 56 {'-fg': 'blue', '-padding': '1 2 3 4'}) 57 58 # check tuple formatting (same as list) 59 check_against( 60 ttk._format_optdict({'test': (1, 2, '', 0)}), 61 {'-test': '1 2 {} 0'}) 62 63 # check untouched values 64 check_against( 65 ttk._format_optdict({'test': {'left': 'as is'}}), 66 {'-test': {'left': 'as is'}}) 67 68 # check script formatting 69 check_against( 70 ttk._format_optdict( 71 {'test': [1, -1, '', '2m', 0], 'test2': 3, 72 'test3': '', 'test4': 'abc def', 73 'test5': '"abc"', 'test6': '{}', 74 'test7': '} -spam {'}, script=True), 75 {'-test': '{1 -1 {} 2m 0}', '-test2': '3', 76 '-test3': '{}', '-test4': '{abc def}', 77 '-test5': '{"abc"}', '-test6': r'\{\}', 78 '-test7': r'\}\ -spam\ \{'}) 79 80 opts = {'αβγ': True, 'á': False} 81 orig_opts = opts.copy() 82 # check if giving unicode keys is fine 83 check_against(ttk._format_optdict(opts), {'-αβγ': True, '-á': False}) 84 # opts should remain unchanged 85 self.assertEqual(opts, orig_opts) 86 87 # passing values with spaces inside a tuple/list 88 check_against( 89 ttk._format_optdict( 90 {'option': ('one two', 'three')}), 91 {'-option': '{one two} three'}) 92 check_against( 93 ttk._format_optdict( 94 {'option': ('one\ttwo', 'three')}), 95 {'-option': '{one\ttwo} three'}) 96 97 # passing empty strings inside a tuple/list 98 check_against( 99 ttk._format_optdict( 100 {'option': ('', 'one')}), 101 {'-option': '{} one'}) 102 103 # passing values with braces inside a tuple/list 104 check_against( 105 ttk._format_optdict( 106 {'option': ('one} {two', 'three')}), 107 {'-option': r'one\}\ \{two three'}) 108 109 # passing quoted strings inside a tuple/list 110 check_against( 111 ttk._format_optdict( 112 {'option': ('"one"', 'two')}), 113 {'-option': '{"one"} two'}) 114 check_against( 115 ttk._format_optdict( 116 {'option': ('{one}', 'two')}), 117 {'-option': r'\{one\} two'}) 118 119 # ignore an option 120 amount_opts = len(ttk._format_optdict(opts, ignore=('á'))) / 2 121 self.assertEqual(amount_opts, len(opts) - 1) 122 123 # ignore non-existing options 124 amount_opts = len(ttk._format_optdict(opts, ignore=('á', 'b'))) / 2 125 self.assertEqual(amount_opts, len(opts) - 1) 126 127 # ignore every option 128 self.assertFalse(ttk._format_optdict(opts, ignore=list(opts.keys()))) 129 130 131 def test_format_mapdict(self): 132 opts = {'a': [('b', 'c', 'val'), ('d', 'otherval'), ('', 'single')]} 133 result = ttk._format_mapdict(opts) 134 self.assertEqual(len(result), len(list(opts.keys())) * 2) 135 self.assertEqual(result, ('-a', '{b c} val d otherval {} single')) 136 self.assertEqual(ttk._format_mapdict(opts, script=True), 137 ('-a', '{{b c} val d otherval {} single}')) 138 139 self.assertEqual(ttk._format_mapdict({2: []}), ('-2', '')) 140 141 opts = {'üñíćódè': [('á', 'vãl')]} 142 result = ttk._format_mapdict(opts) 143 self.assertEqual(result, ('-üñíćódè', 'á vãl')) 144 145 self.assertEqual(ttk._format_mapdict({'opt': [('value',)]}), 146 ('-opt', '{} value')) 147 148 # empty states 149 valid = {'opt': [('', '', 'hi')]} 150 self.assertEqual(ttk._format_mapdict(valid), ('-opt', '{ } hi')) 151 152 # when passing multiple states, they all must be strings 153 invalid = {'opt': [(1, 2, 'valid val')]} 154 self.assertRaises(TypeError, ttk._format_mapdict, invalid) 155 invalid = {'opt': [([1], '2', 'valid val')]} 156 self.assertRaises(TypeError, ttk._format_mapdict, invalid) 157 # but when passing a single state, it can be anything 158 valid = {'opt': [[1, 'value']]} 159 self.assertEqual(ttk._format_mapdict(valid), ('-opt', '1 value')) 160 # special attention to single states which evaluate to False 161 for stateval in (None, 0, False, '', set()): # just some samples 162 valid = {'opt': [(stateval, 'value')]} 163 self.assertEqual(ttk._format_mapdict(valid), 164 ('-opt', '{} value')) 165 166 # values must be iterable 167 opts = {'a': None} 168 self.assertRaises(TypeError, ttk._format_mapdict, opts) 169 170 171 def test_format_elemcreate(self): 172 self.assertTrue(ttk._format_elemcreate(None), (None, ())) 173 174 ## Testing type = image 175 # image type expects at least an image name, so this should raise 176 # IndexError since it tries to access the index 0 of an empty tuple 177 self.assertRaises(IndexError, ttk._format_elemcreate, 'image') 178 179 # don't format returned values as a tcl script 180 # minimum acceptable for image type 181 self.assertEqual(ttk._format_elemcreate('image', False, 'test'), 182 ("test", ())) 183 # specifying a state spec 184 self.assertEqual(ttk._format_elemcreate('image', False, 'test', 185 ('', 'a')), ("test {} a", ())) 186 # state spec with multiple states 187 self.assertEqual(ttk._format_elemcreate('image', False, 'test', 188 ('a', 'b', 'c')), ("test {a b} c", ())) 189 # state spec and options 190 self.assertEqual(ttk._format_elemcreate('image', False, 'test', 191 ('a', 'b'), a='x'), ("test a b", ("-a", "x"))) 192 # format returned values as a tcl script 193 # state spec with multiple states and an option with a multivalue 194 self.assertEqual(ttk._format_elemcreate('image', True, 'test', 195 ('a', 'b', 'c', 'd'), x=[2, 3]), ("{test {a b c} d}", "-x {2 3}")) 196 197 ## Testing type = vsapi 198 # vsapi type expects at least a class name and a part_id, so this 199 # should raise a ValueError since it tries to get two elements from 200 # an empty tuple 201 self.assertRaises(ValueError, ttk._format_elemcreate, 'vsapi') 202 203 # don't format returned values as a tcl script 204 # minimum acceptable for vsapi 205 self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b'), 206 ('a', 'b', ('', 1), ())) 207 # now with a state spec with multiple states 208 self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', 209 [('a', 'b', 'c')]), ('a', 'b', ('a b', 'c'), ())) 210 # state spec and option 211 self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', 212 [('a', 'b')], opt='x'), ('a', 'b', ('a', 'b'), ("-opt", "x"))) 213 # format returned values as a tcl script 214 # state spec with a multivalue and an option 215 self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', 216 opt='x'), ("a b {{} 1}", "-opt x")) 217 self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', 218 [('a', 'b', [1, 2])], opt='x'), ("a b {{a b} {1 2}}", "-opt x")) 219 220 # Testing type = from 221 # from type expects at least a type name 222 self.assertRaises(IndexError, ttk._format_elemcreate, 'from') 223 224 self.assertEqual(ttk._format_elemcreate('from', False, 'a'), 225 ('a', ())) 226 self.assertEqual(ttk._format_elemcreate('from', False, 'a', 'b'), 227 ('a', ('b',))) 228 self.assertEqual(ttk._format_elemcreate('from', True, 'a', 'b'), 229 ('a', 'b')) 230 231 232 def test_format_layoutlist(self): 233 def sample(indent=0, indent_size=2): 234 return ttk._format_layoutlist( 235 [('a', {'other': [1, 2, 3], 'children': 236 [('b', {'children': 237 [('c', {'children': 238 [('d', {'nice': 'opt'})], 'something': (1, 2) 239 })] 240 })] 241 })], indent=indent, indent_size=indent_size)[0] 242 243 def sample_expected(indent=0, indent_size=2): 244 spaces = lambda amount=0: ' ' * (amount + indent) 245 return ( 246 "%sa -other {1 2 3} -children {\n" 247 "%sb -children {\n" 248 "%sc -something {1 2} -children {\n" 249 "%sd -nice opt\n" 250 "%s}\n" 251 "%s}\n" 252 "%s}" % (spaces(), spaces(indent_size), 253 spaces(2 * indent_size), spaces(3 * indent_size), 254 spaces(2 * indent_size), spaces(indent_size), spaces())) 255 256 # empty layout 257 self.assertEqual(ttk._format_layoutlist([])[0], '') 258 259 # _format_layoutlist always expects the second item (in every item) 260 # to act like a dict (except when the value evaluates to False). 261 self.assertRaises(AttributeError, 262 ttk._format_layoutlist, [('a', 'b')]) 263 264 smallest = ttk._format_layoutlist([('a', None)], indent=0) 265 self.assertEqual(smallest, 266 ttk._format_layoutlist([('a', '')], indent=0)) 267 self.assertEqual(smallest[0], 'a') 268 269 # testing indentation levels 270 self.assertEqual(sample(), sample_expected()) 271 for i in range(4): 272 self.assertEqual(sample(i), sample_expected(i)) 273 self.assertEqual(sample(i, i), sample_expected(i, i)) 274 275 # invalid layout format, different kind of exceptions will be 276 # raised by internal functions 277 278 # plain wrong format 279 self.assertRaises(ValueError, ttk._format_layoutlist, 280 ['bad', 'format']) 281 # will try to use iteritems in the 'bad' string 282 self.assertRaises(AttributeError, ttk._format_layoutlist, 283 [('name', 'bad')]) 284 # bad children formatting 285 self.assertRaises(ValueError, ttk._format_layoutlist, 286 [('name', {'children': {'a': None}})]) 287 288 289 def test_script_from_settings(self): 290 # empty options 291 self.assertFalse(ttk._script_from_settings({'name': 292 {'configure': None, 'map': None, 'element create': None}})) 293 294 # empty layout 295 self.assertEqual( 296 ttk._script_from_settings({'name': {'layout': None}}), 297 "ttk::style layout name {\nnull\n}") 298 299 configdict = {'αβγ': True, 'á': False} 300 self.assertTrue( 301 ttk._script_from_settings({'name': {'configure': configdict}})) 302 303 mapdict = {'üñíćódè': [('á', 'vãl')]} 304 self.assertTrue( 305 ttk._script_from_settings({'name': {'map': mapdict}})) 306 307 # invalid image element 308 self.assertRaises(IndexError, 309 ttk._script_from_settings, {'name': {'element create': ['image']}}) 310 311 # minimal valid image 312 self.assertTrue(ttk._script_from_settings({'name': 313 {'element create': ['image', 'name']}})) 314 315 image = {'thing': {'element create': 316 ['image', 'name', ('state1', 'state2', 'val')]}} 317 self.assertEqual(ttk._script_from_settings(image), 318 "ttk::style element create thing image {name {state1 state2} val} ") 319 320 image['thing']['element create'].append({'opt': 30}) 321 self.assertEqual(ttk._script_from_settings(image), 322 "ttk::style element create thing image {name {state1 state2} val} " 323 "-opt 30") 324 325 image['thing']['element create'][-1]['opt'] = [MockTclObj(3), 326 MockTclObj('2m')] 327 self.assertEqual(ttk._script_from_settings(image), 328 "ttk::style element create thing image {name {state1 state2} val} " 329 "-opt {3 2m}") 330 331 vsapi = {'pin': {'element create': 332 ['vsapi', 'EXPLORERBAR', 3, [ 333 ('pressed', '!selected', 3), 334 ('active', '!selected', 2), 335 ('pressed', 'selected', 6), 336 ('active', 'selected', 5), 337 ('selected', 4), 338 ('', 1)]]}} 339 self.assertEqual(ttk._script_from_settings(vsapi), 340 "ttk::style element create pin vsapi EXPLORERBAR 3 {" 341 "{pressed !selected} 3 " 342 "{active !selected} 2 " 343 "{pressed selected} 6 " 344 "{active selected} 5 " 345 "selected 4 " 346 "{} 1} ") 347 348 def test_tclobj_to_py(self): 349 self.assertEqual( 350 ttk._tclobj_to_py((MockStateSpec('a', 'b'), 'val')), 351 [('a', 'b', 'val')]) 352 self.assertEqual( 353 ttk._tclobj_to_py([MockTclObj('1'), 2, MockTclObj('3m')]), 354 [1, 2, '3m']) 355 356 357 def test_list_from_statespec(self): 358 def test_it(sspec, value, res_value, states): 359 self.assertEqual(ttk._list_from_statespec( 360 (sspec, value)), [states + (res_value, )]) 361 362 states_even = tuple('state%d' % i for i in range(6)) 363 statespec = MockStateSpec(*states_even) 364 test_it(statespec, 'val', 'val', states_even) 365 test_it(statespec, MockTclObj('val'), 'val', states_even) 366 367 states_odd = tuple('state%d' % i for i in range(5)) 368 statespec = MockStateSpec(*states_odd) 369 test_it(statespec, 'val', 'val', states_odd) 370 371 test_it(('a', 'b', 'c'), MockTclObj('val'), 'val', ('a', 'b', 'c')) 372 373 374 def test_list_from_layouttuple(self): 375 tk = MockTkApp() 376 377 # empty layout tuple 378 self.assertFalse(ttk._list_from_layouttuple(tk, ())) 379 380 # shortest layout tuple 381 self.assertEqual(ttk._list_from_layouttuple(tk, ('name', )), 382 [('name', {})]) 383 384 # not so interesting ltuple 385 sample_ltuple = ('name', '-option', 'value') 386 self.assertEqual(ttk._list_from_layouttuple(tk, sample_ltuple), 387 [('name', {'option': 'value'})]) 388 389 # empty children 390 self.assertEqual(ttk._list_from_layouttuple(tk, 391 ('something', '-children', ())), 392 [('something', {'children': []})] 393 ) 394 395 # more interesting ltuple 396 ltuple = ( 397 'name', '-option', 'niceone', '-children', ( 398 ('otherone', '-children', ( 399 ('child', )), '-otheropt', 'othervalue' 400 ) 401 ) 402 ) 403 self.assertEqual(ttk._list_from_layouttuple(tk, ltuple), 404 [('name', {'option': 'niceone', 'children': 405 [('otherone', {'otheropt': 'othervalue', 'children': 406 [('child', {})] 407 })] 408 })] 409 ) 410 411 # bad tuples 412 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 413 ('name', 'no_minus')) 414 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 415 ('name', 'no_minus', 'value')) 416 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 417 ('something', '-children')) # no children 418 419 420 def test_val_or_dict(self): 421 def func(res, opt=None, val=None): 422 if opt is None: 423 return res 424 if val is None: 425 return "test val" 426 return (opt, val) 427 428 tk = MockTkApp() 429 tk.call = func 430 431 self.assertEqual(ttk._val_or_dict(tk, {}, '-test:3'), 432 {'test': '3'}) 433 self.assertEqual(ttk._val_or_dict(tk, {}, ('-test', 3)), 434 {'test': 3}) 435 436 self.assertEqual(ttk._val_or_dict(tk, {'test': None}, 'x:y'), 437 'test val') 438 439 self.assertEqual(ttk._val_or_dict(tk, {'test': 3}, 'x:y'), 440 {'test': 3}) 441 442 443 def test_convert_stringval(self): 444 tests = ( 445 (0, 0), ('09', 9), ('a', 'a'), ('áÚ', 'áÚ'), ([], '[]'), 446 (None, 'None') 447 ) 448 for orig, expected in tests: 449 self.assertEqual(ttk._convert_stringval(orig), expected) 450 451 452class TclObjsToPyTest(unittest.TestCase): 453 454 def test_unicode(self): 455 adict = {'opt': 'välúè'} 456 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': 'välúè'}) 457 458 adict['opt'] = MockTclObj(adict['opt']) 459 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': 'välúè'}) 460 461 def test_multivalues(self): 462 adict = {'opt': [1, 2, 3, 4]} 463 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': [1, 2, 3, 4]}) 464 465 adict['opt'] = [1, 'xm', 3] 466 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': [1, 'xm', 3]}) 467 468 adict['opt'] = (MockStateSpec('a', 'b'), 'válũè') 469 self.assertEqual(ttk.tclobjs_to_py(adict), 470 {'opt': [('a', 'b', 'válũè')]}) 471 472 self.assertEqual(ttk.tclobjs_to_py({'x': ['y z']}), 473 {'x': ['y z']}) 474 475 def test_nosplit(self): 476 self.assertEqual(ttk.tclobjs_to_py({'text': 'some text'}), 477 {'text': 'some text'}) 478 479 480if __name__ == '__main__': 481 unittest.main() 482