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 self.assertEqual(ttk._format_mapdict({'opt': [('value',)]}), 141 ('-opt', '{} value')) 142 143 # empty states 144 valid = {'opt': [('', '', 'hi')]} 145 self.assertEqual(ttk._format_mapdict(valid), ('-opt', '{ } hi')) 146 147 # when passing multiple states, they all must be strings 148 invalid = {'opt': [(1, 2, 'valid val')]} 149 self.assertRaises(TypeError, ttk._format_mapdict, invalid) 150 invalid = {'opt': [([1], '2', 'valid val')]} 151 self.assertRaises(TypeError, ttk._format_mapdict, invalid) 152 # but when passing a single state, it can be anything 153 valid = {'opt': [[1, 'value']]} 154 self.assertEqual(ttk._format_mapdict(valid), ('-opt', '1 value')) 155 # special attention to single states which evaluate to False 156 for stateval in (None, 0, False, '', set()): # just some samples 157 valid = {'opt': [(stateval, 'value')]} 158 self.assertEqual(ttk._format_mapdict(valid), 159 ('-opt', '{} value')) 160 161 # values must be iterable 162 opts = {'a': None} 163 self.assertRaises(TypeError, ttk._format_mapdict, opts) 164 165 166 def test_format_elemcreate(self): 167 self.assertTrue(ttk._format_elemcreate(None), (None, ())) 168 169 ## Testing type = image 170 # image type expects at least an image name, so this should raise 171 # IndexError since it tries to access the index 0 of an empty tuple 172 self.assertRaises(IndexError, ttk._format_elemcreate, 'image') 173 174 # don't format returned values as a tcl script 175 # minimum acceptable for image type 176 self.assertEqual(ttk._format_elemcreate('image', False, 'test'), 177 ("test ", ())) 178 # specifying a state spec 179 self.assertEqual(ttk._format_elemcreate('image', False, 'test', 180 ('', 'a')), ("test {} a", ())) 181 # state spec with multiple states 182 self.assertEqual(ttk._format_elemcreate('image', False, 'test', 183 ('a', 'b', 'c')), ("test {a b} c", ())) 184 # state spec and options 185 self.assertEqual(ttk._format_elemcreate('image', False, 'test', 186 ('a', 'b'), a='x'), ("test a b", ("-a", "x"))) 187 # format returned values as a tcl script 188 # state spec with multiple states and an option with a multivalue 189 self.assertEqual(ttk._format_elemcreate('image', True, 'test', 190 ('a', 'b', 'c', 'd'), x=[2, 3]), ("{test {a b c} d}", "-x {2 3}")) 191 192 ## Testing type = vsapi 193 # vsapi type expects at least a class name and a part_id, so this 194 # should raise a ValueError since it tries to get two elements from 195 # an empty tuple 196 self.assertRaises(ValueError, ttk._format_elemcreate, 'vsapi') 197 198 # don't format returned values as a tcl script 199 # minimum acceptable for vsapi 200 self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b'), 201 ("a b ", ())) 202 # now with a state spec with multiple states 203 self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', 204 ('a', 'b', 'c')), ("a b {a b} c", ())) 205 # state spec and option 206 self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', 207 ('a', 'b'), opt='x'), ("a b a b", ("-opt", "x"))) 208 # format returned values as a tcl script 209 # state spec with a multivalue and an option 210 self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', 211 ('a', 'b', [1, 2]), opt='x'), ("{a b {a b} {1 2}}", "-opt x")) 212 213 # Testing type = from 214 # from type expects at least a type name 215 self.assertRaises(IndexError, ttk._format_elemcreate, 'from') 216 217 self.assertEqual(ttk._format_elemcreate('from', False, 'a'), 218 ('a', ())) 219 self.assertEqual(ttk._format_elemcreate('from', False, 'a', 'b'), 220 ('a', ('b', ))) 221 self.assertEqual(ttk._format_elemcreate('from', True, 'a', 'b'), 222 ('{a}', 'b')) 223 224 225 def test_format_layoutlist(self): 226 def sample(indent=0, indent_size=2): 227 return ttk._format_layoutlist( 228 [('a', {'other': [1, 2, 3], 'children': 229 [('b', {'children': 230 [('c', {'children': 231 [('d', {'nice': 'opt'})], 'something': (1, 2) 232 })] 233 })] 234 })], indent=indent, indent_size=indent_size)[0] 235 236 def sample_expected(indent=0, indent_size=2): 237 spaces = lambda amount=0: ' ' * (amount + indent) 238 return ( 239 "%sa -other {1 2 3} -children {\n" 240 "%sb -children {\n" 241 "%sc -something {1 2} -children {\n" 242 "%sd -nice opt\n" 243 "%s}\n" 244 "%s}\n" 245 "%s}" % (spaces(), spaces(indent_size), 246 spaces(2 * indent_size), spaces(3 * indent_size), 247 spaces(2 * indent_size), spaces(indent_size), spaces())) 248 249 # empty layout 250 self.assertEqual(ttk._format_layoutlist([])[0], '') 251 252 # _format_layoutlist always expects the second item (in every item) 253 # to act like a dict (except when the value evaluates to False). 254 self.assertRaises(AttributeError, 255 ttk._format_layoutlist, [('a', 'b')]) 256 257 smallest = ttk._format_layoutlist([('a', None)], indent=0) 258 self.assertEqual(smallest, 259 ttk._format_layoutlist([('a', '')], indent=0)) 260 self.assertEqual(smallest[0], 'a') 261 262 # testing indentation levels 263 self.assertEqual(sample(), sample_expected()) 264 for i in range(4): 265 self.assertEqual(sample(i), sample_expected(i)) 266 self.assertEqual(sample(i, i), sample_expected(i, i)) 267 268 # invalid layout format, different kind of exceptions will be 269 # raised by internal functions 270 271 # plain wrong format 272 self.assertRaises(ValueError, ttk._format_layoutlist, 273 ['bad', 'format']) 274 # will try to use iteritems in the 'bad' string 275 self.assertRaises(AttributeError, ttk._format_layoutlist, 276 [('name', 'bad')]) 277 # bad children formatting 278 self.assertRaises(ValueError, ttk._format_layoutlist, 279 [('name', {'children': {'a': None}})]) 280 281 282 def test_script_from_settings(self): 283 # empty options 284 self.assertFalse(ttk._script_from_settings({'name': 285 {'configure': None, 'map': None, 'element create': None}})) 286 287 # empty layout 288 self.assertEqual( 289 ttk._script_from_settings({'name': {'layout': None}}), 290 "ttk::style layout name {\nnull\n}") 291 292 configdict = {'αβγ': True, 'á': False} 293 self.assertTrue( 294 ttk._script_from_settings({'name': {'configure': configdict}})) 295 296 mapdict = {'üñíćódè': [('á', 'vãl')]} 297 self.assertTrue( 298 ttk._script_from_settings({'name': {'map': mapdict}})) 299 300 # invalid image element 301 self.assertRaises(IndexError, 302 ttk._script_from_settings, {'name': {'element create': ['image']}}) 303 304 # minimal valid image 305 self.assertTrue(ttk._script_from_settings({'name': 306 {'element create': ['image', 'name']}})) 307 308 image = {'thing': {'element create': 309 ['image', 'name', ('state1', 'state2', 'val')]}} 310 self.assertEqual(ttk._script_from_settings(image), 311 "ttk::style element create thing image {name {state1 state2} val} ") 312 313 image['thing']['element create'].append({'opt': 30}) 314 self.assertEqual(ttk._script_from_settings(image), 315 "ttk::style element create thing image {name {state1 state2} val} " 316 "-opt 30") 317 318 image['thing']['element create'][-1]['opt'] = [MockTclObj(3), 319 MockTclObj('2m')] 320 self.assertEqual(ttk._script_from_settings(image), 321 "ttk::style element create thing image {name {state1 state2} val} " 322 "-opt {3 2m}") 323 324 325 def test_tclobj_to_py(self): 326 self.assertEqual( 327 ttk._tclobj_to_py((MockStateSpec('a', 'b'), 'val')), 328 [('a', 'b', 'val')]) 329 self.assertEqual( 330 ttk._tclobj_to_py([MockTclObj('1'), 2, MockTclObj('3m')]), 331 [1, 2, '3m']) 332 333 334 def test_list_from_statespec(self): 335 def test_it(sspec, value, res_value, states): 336 self.assertEqual(ttk._list_from_statespec( 337 (sspec, value)), [states + (res_value, )]) 338 339 states_even = tuple('state%d' % i for i in range(6)) 340 statespec = MockStateSpec(*states_even) 341 test_it(statespec, 'val', 'val', states_even) 342 test_it(statespec, MockTclObj('val'), 'val', states_even) 343 344 states_odd = tuple('state%d' % i for i in range(5)) 345 statespec = MockStateSpec(*states_odd) 346 test_it(statespec, 'val', 'val', states_odd) 347 348 test_it(('a', 'b', 'c'), MockTclObj('val'), 'val', ('a', 'b', 'c')) 349 350 351 def test_list_from_layouttuple(self): 352 tk = MockTkApp() 353 354 # empty layout tuple 355 self.assertFalse(ttk._list_from_layouttuple(tk, ())) 356 357 # shortest layout tuple 358 self.assertEqual(ttk._list_from_layouttuple(tk, ('name', )), 359 [('name', {})]) 360 361 # not so interesting ltuple 362 sample_ltuple = ('name', '-option', 'value') 363 self.assertEqual(ttk._list_from_layouttuple(tk, sample_ltuple), 364 [('name', {'option': 'value'})]) 365 366 # empty children 367 self.assertEqual(ttk._list_from_layouttuple(tk, 368 ('something', '-children', ())), 369 [('something', {'children': []})] 370 ) 371 372 # more interesting ltuple 373 ltuple = ( 374 'name', '-option', 'niceone', '-children', ( 375 ('otherone', '-children', ( 376 ('child', )), '-otheropt', 'othervalue' 377 ) 378 ) 379 ) 380 self.assertEqual(ttk._list_from_layouttuple(tk, ltuple), 381 [('name', {'option': 'niceone', 'children': 382 [('otherone', {'otheropt': 'othervalue', 'children': 383 [('child', {})] 384 })] 385 })] 386 ) 387 388 # bad tuples 389 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 390 ('name', 'no_minus')) 391 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 392 ('name', 'no_minus', 'value')) 393 self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, 394 ('something', '-children')) # no children 395 396 397 def test_val_or_dict(self): 398 def func(res, opt=None, val=None): 399 if opt is None: 400 return res 401 if val is None: 402 return "test val" 403 return (opt, val) 404 405 tk = MockTkApp() 406 tk.call = func 407 408 self.assertEqual(ttk._val_or_dict(tk, {}, '-test:3'), 409 {'test': '3'}) 410 self.assertEqual(ttk._val_or_dict(tk, {}, ('-test', 3)), 411 {'test': 3}) 412 413 self.assertEqual(ttk._val_or_dict(tk, {'test': None}, 'x:y'), 414 'test val') 415 416 self.assertEqual(ttk._val_or_dict(tk, {'test': 3}, 'x:y'), 417 {'test': 3}) 418 419 420 def test_convert_stringval(self): 421 tests = ( 422 (0, 0), ('09', 9), ('a', 'a'), ('áÚ', 'áÚ'), ([], '[]'), 423 (None, 'None') 424 ) 425 for orig, expected in tests: 426 self.assertEqual(ttk._convert_stringval(orig), expected) 427 428 429class TclObjsToPyTest(unittest.TestCase): 430 431 def test_unicode(self): 432 adict = {'opt': 'välúè'} 433 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': 'välúè'}) 434 435 adict['opt'] = MockTclObj(adict['opt']) 436 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': 'välúè'}) 437 438 def test_multivalues(self): 439 adict = {'opt': [1, 2, 3, 4]} 440 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': [1, 2, 3, 4]}) 441 442 adict['opt'] = [1, 'xm', 3] 443 self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': [1, 'xm', 3]}) 444 445 adict['opt'] = (MockStateSpec('a', 'b'), 'válũè') 446 self.assertEqual(ttk.tclobjs_to_py(adict), 447 {'opt': [('a', 'b', 'válũè')]}) 448 449 self.assertEqual(ttk.tclobjs_to_py({'x': ['y z']}), 450 {'x': ['y z']}) 451 452 def test_nosplit(self): 453 self.assertEqual(ttk.tclobjs_to_py({'text': 'some text'}), 454 {'text': 'some text'}) 455 456tests_nogui = (InternalFunctionsTest, TclObjsToPyTest) 457 458if __name__ == "__main__": 459 from test.support import run_unittest 460 run_unittest(*tests_nogui) 461