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 ", ())) 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 ('a', 'b', [1, 2]), opt='x'), ("{a b {a b} {1 2}}", "-opt x")) 217 218 # Testing type = from 219 # from type expects at least a type name 220 self.assertRaises(IndexError, ttk._format_elemcreate, 'from') 221 222 self.assertEqual(ttk._format_elemcreate('from', False, 'a'), 223 ('a', ())) 224 self.assertEqual(ttk._format_elemcreate('from', False, 'a', 'b'), 225 ('a', ('b', ))) 226 self.assertEqual(ttk._format_elemcreate('from', True, 'a', 'b'), 227 ('{a}', 'b')) 228 229 230 def test_format_layoutlist(self): 231 def sample(indent=0, indent_size=2): 232 return ttk._format_layoutlist( 233 [('a', {'other': [1, 2, 3], 'children': 234 [('b', {'children': 235 [('c', {'children': 236 [('d', {'nice': 'opt'})], 'something': (1, 2) 237 })] 238 })] 239 })], indent=indent, indent_size=indent_size)[0] 240 241 def sample_expected(indent=0, indent_size=2): 242 spaces = lambda amount=0: ' ' * (amount + indent) 243 return ( 244 "%sa -other {1 2 3} -children {\n" 245 "%sb -children {\n" 246 "%sc -something {1 2} -children {\n" 247 "%sd -nice opt\n" 248 "%s}\n" 249 "%s}\n" 250 "%s}" % (spaces(), spaces(indent_size), 251 spaces(2 * indent_size), spaces(3 * indent_size), 252 spaces(2 * indent_size), spaces(indent_size), spaces())) 253 254 # empty layout 255 self.assertEqual(ttk._format_layoutlist([])[0], '') 256 257 # _format_layoutlist always expects the second item (in every item) 258 # to act like a dict (except when the value evaluates to False). 259 self.assertRaises(AttributeError, 260 ttk._format_layoutlist, [('a', 'b')]) 261 262 smallest = ttk._format_layoutlist([('a', None)], indent=0) 263 self.assertEqual(smallest, 264 ttk._format_layoutlist([('a', '')], indent=0)) 265 self.assertEqual(smallest[0], 'a') 266 267 # testing indentation levels 268 self.assertEqual(sample(), sample_expected()) 269 for i in range(4): 270 self.assertEqual(sample(i), sample_expected(i)) 271 self.assertEqual(sample(i, i), sample_expected(i, i)) 272 273 # invalid layout format, different kind of exceptions will be 274 # raised by internal functions 275 276 # plain wrong format 277 self.assertRaises(ValueError, ttk._format_layoutlist, 278 ['bad', 'format']) 279 # will try to use iteritems in the 'bad' string 280 self.assertRaises(AttributeError, ttk._format_layoutlist, 281 [('name', 'bad')]) 282 # bad children formatting 283 self.assertRaises(ValueError, ttk._format_layoutlist, 284 [('name', {'children': {'a': None}})]) 285 286 287 def test_script_from_settings(self): 288 # empty options 289 self.assertFalse(ttk._script_from_settings({'name': 290 {'configure': None, 'map': None, 'element create': None}})) 291 292 # empty layout 293 self.assertEqual( 294 ttk._script_from_settings({'name': {'layout': None}}), 295 "ttk::style layout name {\nnull\n}") 296 297 configdict = {'αβγ': True, 'á': False} 298 self.assertTrue( 299 ttk._script_from_settings({'name': {'configure': configdict}})) 300 301 mapdict = {'üñíćódè': [('á', 'vãl')]} 302 self.assertTrue( 303 ttk._script_from_settings({'name': {'map': mapdict}})) 304 305 # invalid image element 306 self.assertRaises(IndexError, 307 ttk._script_from_settings, {'name': {'element create': ['image']}}) 308 309 # minimal valid image 310 self.assertTrue(ttk._script_from_settings({'name': 311 {'element create': ['image', 'name']}})) 312 313 image = {'thing': {'element create': 314 ['image', 'name', ('state1', 'state2', 'val')]}} 315 self.assertEqual(ttk._script_from_settings(image), 316 "ttk::style element create thing image {name {state1 state2} val} ") 317 318 image['thing']['element create'].append({'opt': 30}) 319 self.assertEqual(ttk._script_from_settings(image), 320 "ttk::style element create thing image {name {state1 state2} val} " 321 "-opt 30") 322 323 image['thing']['element create'][-1]['opt'] = [MockTclObj(3), 324 MockTclObj('2m')] 325 self.assertEqual(ttk._script_from_settings(image), 326 "ttk::style element create thing image {name {state1 state2} val} " 327 "-opt {3 2m}") 328 329 330 def test_tclobj_to_py(self): 331 self.assertEqual( 332 ttk._tclobj_to_py((MockStateSpec('a', 'b'), 'val')), 333 [('a', 'b', 'val')]) 334 self.assertEqual( 335 ttk._tclobj_to_py([MockTclObj('1'), 2, MockTclObj('3m')]), 336 [1, 2, '3m']) 337 338 339 def test_list_from_statespec(self): 340 def test_it(sspec, value, res_value, states): 341 self.assertEqual(ttk._list_from_statespec( 342 (sspec, value)), [states + (res_value, )]) 343 344 states_even = tuple('state%d' % i for i in range(6)) 345 statespec = MockStateSpec(*states_even) 346 test_it(statespec, 'val', 'val', states_even) 347 test_it(statespec, MockTclObj('val'), 'val', states_even) 348 349 states_odd = tuple('state%d' % i for i in range(5)) 350 statespec = MockStateSpec(*states_odd) 351 test_it(statespec, 'val', 'val', states_odd) 352 353 test_it(('a', 'b', 'c'), MockTclObj('val'), 'val', ('a', 'b', 'c')) 354 355 356 def test_list_from_layouttuple(self): 357 tk = MockTkApp() 358 359 # empty layout tuple 360 self.assertFalse(ttk._list_from_layouttuple(tk, ())) 361 362 # shortest layout tuple 363 self.assertEqual(ttk._list_from_layouttuple(tk, ('name', )), 364 [('name', {})]) 365 366 # not so interesting ltuple 367 sample_ltuple = ('name', '-option', 'value') 368 self.assertEqual(ttk._list_from_layouttuple(tk, sample_ltuple), 369 [('name', {'option': 'value'})]) 370 371 # empty children 372 self.assertEqual(ttk._list_from_layouttuple(tk, 373 ('something', '-children', ())), 374 [('something', {'children': []})] 375 ) 376 377 # more interesting ltuple 378 ltuple = ( 379 'name', '-option', 'niceone', '-children', ( 380 ('otherone', '-children', ( 381 ('child', )), '-otheropt', 'othervalue' 382 ) 383 ) 384 ) 385 self.assertEqual(ttk._list_from_layouttuple(tk, ltuple), 386 [('name', {'option': 'niceone', 'children': 387 [('otherone', {'otheropt': 'othervalue', 'children': 388 [('child', {})] 389 })] 390 })] 391 ) 392 393 # bad tuples 394 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 395 ('name', 'no_minus')) 396 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 397 ('name', 'no_minus', 'value')) 398 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 399 ('something', '-children')) # no children 400 401 402 def test_val_or_dict(self): 403 def func(res, opt=None, val=None): 404 if opt is None: 405 return res 406 if val is None: 407 return "test val" 408 return (opt, val) 409 410 tk = MockTkApp() 411 tk.call = func 412 413 self.assertEqual(ttk._val_or_dict(tk, {}, '-test:3'), 414 {'test': '3'}) 415 self.assertEqual(ttk._val_or_dict(tk, {}, ('-test', 3)), 416 {'test': 3}) 417 418 self.assertEqual(ttk._val_or_dict(tk, {'test': None}, 'x:y'), 419 'test val') 420 421 self.assertEqual(ttk._val_or_dict(tk, {'test': 3}, 'x:y'), 422 {'test': 3}) 423 424 425 def test_convert_stringval(self): 426 tests = ( 427 (0, 0), ('09', 9), ('a', 'a'), ('áÚ', 'áÚ'), ([], '[]'), 428 (None, 'None') 429 ) 430 for orig, expected in tests: 431 self.assertEqual(ttk._convert_stringval(orig), expected) 432 433 434class TclObjsToPyTest(unittest.TestCase): 435 436 def test_unicode(self): 437 adict = {'opt': 'välúè'} 438 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': 'välúè'}) 439 440 adict['opt'] = MockTclObj(adict['opt']) 441 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': 'välúè'}) 442 443 def test_multivalues(self): 444 adict = {'opt': [1, 2, 3, 4]} 445 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': [1, 2, 3, 4]}) 446 447 adict['opt'] = [1, 'xm', 3] 448 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': [1, 'xm', 3]}) 449 450 adict['opt'] = (MockStateSpec('a', 'b'), 'válũè') 451 self.assertEqual(ttk.tclobjs_to_py(adict), 452 {'opt': [('a', 'b', 'válũè')]}) 453 454 self.assertEqual(ttk.tclobjs_to_py({'x': ['y z']}), 455 {'x': ['y z']}) 456 457 def test_nosplit(self): 458 self.assertEqual(ttk.tclobjs_to_py({'text': 'some text'}), 459 {'text': 'some text'}) 460 461 462if __name__ == '__main__': 463 unittest.main() 464