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