1import unittest
2import locale
3import re
4import subprocess
5import sys
6import os
7import warnings
8from test import support
9from test.support import import_helper
10from test.support import os_helper
11
12# Skip this test if the _tkinter module wasn't built.
13_tkinter = import_helper.import_module('_tkinter')
14
15import tkinter
16from tkinter import Tcl
17from _tkinter import TclError
18
19try:
20    from _testcapi import INT_MAX, PY_SSIZE_T_MAX
21except ImportError:
22    INT_MAX = PY_SSIZE_T_MAX = sys.maxsize
23
24tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.')))
25
26_tk_patchlevel = None
27def get_tk_patchlevel():
28    global _tk_patchlevel
29    if _tk_patchlevel is None:
30        tcl = Tcl()
31        _tk_patchlevel = tcl.info_patchlevel()
32    return _tk_patchlevel
33
34
35class TkinterTest(unittest.TestCase):
36
37    def testFlattenLen(self):
38        # Object without length.
39        self.assertRaises(TypeError, _tkinter._flatten, True)
40        # Object with length, but not sequence.
41        self.assertRaises(TypeError, _tkinter._flatten, {})
42        # Sequence or set, but not tuple or list.
43        # (issue44608: there were leaks in the following cases)
44        self.assertRaises(TypeError, _tkinter._flatten, 'string')
45        self.assertRaises(TypeError, _tkinter._flatten, {'set'})
46
47
48class TclTest(unittest.TestCase):
49
50    def setUp(self):
51        self.interp = Tcl()
52        self.wantobjects = self.interp.tk.wantobjects()
53
54    def testEval(self):
55        tcl = self.interp
56        tcl.eval('set a 1')
57        self.assertEqual(tcl.eval('set a'),'1')
58
59    def test_eval_null_in_result(self):
60        tcl = self.interp
61        self.assertEqual(tcl.eval('set a "a\\0b"'), 'a\x00b')
62
63    def test_eval_surrogates_in_result(self):
64        tcl = self.interp
65        self.assertIn(tcl.eval(r'set a "<\ud83d\udcbb>"'), '<\U0001f4bb>')
66
67    def testEvalException(self):
68        tcl = self.interp
69        self.assertRaises(TclError,tcl.eval,'set a')
70
71    def testEvalException2(self):
72        tcl = self.interp
73        self.assertRaises(TclError,tcl.eval,'this is wrong')
74
75    def testCall(self):
76        tcl = self.interp
77        tcl.call('set','a','1')
78        self.assertEqual(tcl.call('set','a'),'1')
79
80    def testCallException(self):
81        tcl = self.interp
82        self.assertRaises(TclError,tcl.call,'set','a')
83
84    def testCallException2(self):
85        tcl = self.interp
86        self.assertRaises(TclError,tcl.call,'this','is','wrong')
87
88    def testSetVar(self):
89        tcl = self.interp
90        tcl.setvar('a','1')
91        self.assertEqual(tcl.eval('set a'),'1')
92
93    def testSetVarArray(self):
94        tcl = self.interp
95        tcl.setvar('a(1)','1')
96        self.assertEqual(tcl.eval('set a(1)'),'1')
97
98    def testGetVar(self):
99        tcl = self.interp
100        tcl.eval('set a 1')
101        self.assertEqual(tcl.getvar('a'),'1')
102
103    def testGetVarArray(self):
104        tcl = self.interp
105        tcl.eval('set a(1) 1')
106        self.assertEqual(tcl.getvar('a(1)'),'1')
107
108    def testGetVarException(self):
109        tcl = self.interp
110        self.assertRaises(TclError,tcl.getvar,'a')
111
112    def testGetVarArrayException(self):
113        tcl = self.interp
114        self.assertRaises(TclError,tcl.getvar,'a(1)')
115
116    def testUnsetVar(self):
117        tcl = self.interp
118        tcl.setvar('a',1)
119        self.assertEqual(tcl.eval('info exists a'),'1')
120        tcl.unsetvar('a')
121        self.assertEqual(tcl.eval('info exists a'),'0')
122
123    def testUnsetVarArray(self):
124        tcl = self.interp
125        tcl.setvar('a(1)',1)
126        tcl.setvar('a(2)',2)
127        self.assertEqual(tcl.eval('info exists a(1)'),'1')
128        self.assertEqual(tcl.eval('info exists a(2)'),'1')
129        tcl.unsetvar('a(1)')
130        self.assertEqual(tcl.eval('info exists a(1)'),'0')
131        self.assertEqual(tcl.eval('info exists a(2)'),'1')
132
133    def testUnsetVarException(self):
134        tcl = self.interp
135        self.assertRaises(TclError,tcl.unsetvar,'a')
136
137    def get_integers(self):
138        return (0, 1, -1,
139                2**31-1, -2**31, 2**31, -2**31-1,
140                2**63-1, -2**63, 2**63, -2**63-1,
141                2**1000, -2**1000)
142
143    def test_getint(self):
144        tcl = self.interp.tk
145        for i in self.get_integers():
146            self.assertEqual(tcl.getint(' %d ' % i), i)
147            self.assertEqual(tcl.getint(' %#o ' % i), i)
148            # Numbers starting with 0 are parsed as decimal in Tcl 9.0
149            # and as octal in older versions.
150            self.assertEqual(tcl.getint((' %#o ' % i).replace('o', '')),
151                             i if tcl_version < (9, 0) else int('%o' % i))
152            self.assertEqual(tcl.getint(' %#x ' % i), i)
153        self.assertEqual(tcl.getint(42), 42)
154        self.assertRaises(TypeError, tcl.getint)
155        self.assertRaises(TypeError, tcl.getint, '42', '10')
156        self.assertRaises(TypeError, tcl.getint, b'42')
157        self.assertRaises(TypeError, tcl.getint, 42.0)
158        self.assertRaises(TclError, tcl.getint, 'a')
159        self.assertRaises((TypeError, ValueError, TclError),
160                          tcl.getint, '42\0')
161        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
162                          tcl.getint, '42\ud800')
163
164    def test_getdouble(self):
165        tcl = self.interp.tk
166        self.assertEqual(tcl.getdouble(' 42 '), 42.0)
167        self.assertEqual(tcl.getdouble(' 42.5 '), 42.5)
168        self.assertEqual(tcl.getdouble(42.5), 42.5)
169        self.assertEqual(tcl.getdouble(42), 42.0)
170        self.assertRaises(TypeError, tcl.getdouble)
171        self.assertRaises(TypeError, tcl.getdouble, '42.5', '10')
172        self.assertRaises(TypeError, tcl.getdouble, b'42.5')
173        self.assertRaises(TclError, tcl.getdouble, 'a')
174        self.assertRaises((TypeError, ValueError, TclError),
175                          tcl.getdouble, '42.5\0')
176        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
177                          tcl.getdouble, '42.5\ud800')
178
179    def test_getboolean(self):
180        tcl = self.interp.tk
181        self.assertIs(tcl.getboolean('on'), True)
182        self.assertIs(tcl.getboolean('1'), True)
183        self.assertIs(tcl.getboolean(42), True)
184        self.assertIs(tcl.getboolean(0), False)
185        self.assertRaises(TypeError, tcl.getboolean)
186        self.assertRaises(TypeError, tcl.getboolean, 'on', '1')
187        self.assertRaises(TypeError, tcl.getboolean, b'on')
188        self.assertRaises(TypeError, tcl.getboolean, 1.0)
189        self.assertRaises(TclError, tcl.getboolean, 'a')
190        self.assertRaises((TypeError, ValueError, TclError),
191                          tcl.getboolean, 'on\0')
192        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
193                          tcl.getboolean, 'on\ud800')
194
195    def testEvalFile(self):
196        tcl = self.interp
197        filename = os_helper.TESTFN_ASCII
198        self.addCleanup(os_helper.unlink, filename)
199        with open(filename, 'w') as f:
200            f.write("""set a 1
201            set b 2
202            set c [ expr $a + $b ]
203            """)
204        tcl.evalfile(filename)
205        self.assertEqual(tcl.eval('set a'),'1')
206        self.assertEqual(tcl.eval('set b'),'2')
207        self.assertEqual(tcl.eval('set c'),'3')
208
209    def test_evalfile_null_in_result(self):
210        tcl = self.interp
211        filename = os_helper.TESTFN_ASCII
212        self.addCleanup(os_helper.unlink, filename)
213        with open(filename, 'w') as f:
214            f.write("""
215            set a "a\0b"
216            set b "a\\0b"
217            """)
218        tcl.evalfile(filename)
219        self.assertEqual(tcl.eval('set a'), 'a\x00b')
220        self.assertEqual(tcl.eval('set b'), 'a\x00b')
221
222    def test_evalfile_surrogates_in_result(self):
223        tcl = self.interp
224        encoding = tcl.call('encoding', 'system')
225        self.addCleanup(tcl.call, 'encoding', 'system', encoding)
226        tcl.call('encoding', 'system', 'utf-8')
227
228        filename = os_helper.TESTFN_ASCII
229        self.addCleanup(os_helper.unlink, filename)
230        with open(filename, 'wb') as f:
231            f.write(b"""
232            set a "<\xed\xa0\xbd\xed\xb2\xbb>"
233            set b "<\\ud83d\\udcbb>"
234            """)
235        tcl.evalfile(filename)
236        self.assertEqual(tcl.eval('set a'), '<\U0001f4bb>')
237        self.assertEqual(tcl.eval('set b'), '<\U0001f4bb>')
238
239    def testEvalFileException(self):
240        tcl = self.interp
241        filename = "doesnotexists"
242        try:
243            os.remove(filename)
244        except Exception as e:
245            pass
246        self.assertRaises(TclError,tcl.evalfile,filename)
247
248    def testPackageRequireException(self):
249        tcl = self.interp
250        self.assertRaises(TclError,tcl.eval,'package require DNE')
251
252    @unittest.skipUnless(sys.platform == 'win32', 'Requires Windows')
253    def testLoadWithUNC(self):
254        # Build a UNC path from the regular path.
255        # Something like
256        #   \\%COMPUTERNAME%\c$\python27\python.exe
257
258        fullname = os.path.abspath(sys.executable)
259        if fullname[1] != ':':
260            raise unittest.SkipTest('Absolute path should have drive part')
261        unc_name = r'\\%s\%s$\%s' % (os.environ['COMPUTERNAME'],
262                                    fullname[0],
263                                    fullname[3:])
264        if not os.path.exists(unc_name):
265            raise unittest.SkipTest('Cannot connect to UNC Path')
266
267        with os_helper.EnvironmentVarGuard() as env:
268            env.unset("TCL_LIBRARY")
269            stdout = subprocess.check_output(
270                    [unc_name, '-c', 'import tkinter; print(tkinter)'])
271
272        self.assertIn(b'tkinter', stdout)
273
274    def test_exprstring(self):
275        tcl = self.interp
276        tcl.call('set', 'a', 3)
277        tcl.call('set', 'b', 6)
278        def check(expr, expected):
279            result = tcl.exprstring(expr)
280            self.assertEqual(result, expected)
281            self.assertIsInstance(result, str)
282
283        self.assertRaises(TypeError, tcl.exprstring)
284        self.assertRaises(TypeError, tcl.exprstring, '8.2', '+6')
285        self.assertRaises(TypeError, tcl.exprstring, b'8.2 + 6')
286        self.assertRaises(TclError, tcl.exprstring, 'spam')
287        check('', '0')
288        check('8.2 + 6', '14.2')
289        check('3.1 + $a', '6.1')
290        check('2 + "$a.$b"', '5.6')
291        check('4*[llength "6 2"]', '8')
292        check('{word one} < "word $a"', '0')
293        check('4*2 < 7', '0')
294        check('hypot($a, 4)', '5.0')
295        check('5 / 4', '1')
296        check('5 / 4.0', '1.25')
297        check('5 / ( [string length "abcd"] + 0.0 )', '1.25')
298        check('20.0/5.0', '4.0')
299        check('"0x03" > "2"', '1')
300        check('[string length "a\xbd\u20ac"]', '3')
301        check(r'[string length "a\xbd\u20ac"]', '3')
302        check('"abc"', 'abc')
303        check('"a\xbd\u20ac"', 'a\xbd\u20ac')
304        check(r'"a\xbd\u20ac"', 'a\xbd\u20ac')
305        check(r'"a\0b"', 'a\x00b')
306        check('2**64', str(2**64))
307
308    def test_exprdouble(self):
309        tcl = self.interp
310        tcl.call('set', 'a', 3)
311        tcl.call('set', 'b', 6)
312        def check(expr, expected):
313            result = tcl.exprdouble(expr)
314            self.assertEqual(result, expected)
315            self.assertIsInstance(result, float)
316
317        self.assertRaises(TypeError, tcl.exprdouble)
318        self.assertRaises(TypeError, tcl.exprdouble, '8.2', '+6')
319        self.assertRaises(TypeError, tcl.exprdouble, b'8.2 + 6')
320        self.assertRaises(TclError, tcl.exprdouble, 'spam')
321        check('', 0.0)
322        check('8.2 + 6', 14.2)
323        check('3.1 + $a', 6.1)
324        check('2 + "$a.$b"', 5.6)
325        check('4*[llength "6 2"]', 8.0)
326        check('{word one} < "word $a"', 0.0)
327        check('4*2 < 7', 0.0)
328        check('hypot($a, 4)', 5.0)
329        check('5 / 4', 1.0)
330        check('5 / 4.0', 1.25)
331        check('5 / ( [string length "abcd"] + 0.0 )', 1.25)
332        check('20.0/5.0', 4.0)
333        check('"0x03" > "2"', 1.0)
334        check('[string length "a\xbd\u20ac"]', 3.0)
335        check(r'[string length "a\xbd\u20ac"]', 3.0)
336        self.assertRaises(TclError, tcl.exprdouble, '"abc"')
337        check('2**64', float(2**64))
338
339    def test_exprlong(self):
340        tcl = self.interp
341        tcl.call('set', 'a', 3)
342        tcl.call('set', 'b', 6)
343        def check(expr, expected):
344            result = tcl.exprlong(expr)
345            self.assertEqual(result, expected)
346            self.assertIsInstance(result, int)
347
348        self.assertRaises(TypeError, tcl.exprlong)
349        self.assertRaises(TypeError, tcl.exprlong, '8.2', '+6')
350        self.assertRaises(TypeError, tcl.exprlong, b'8.2 + 6')
351        self.assertRaises(TclError, tcl.exprlong, 'spam')
352        check('', 0)
353        check('8.2 + 6', 14)
354        check('3.1 + $a', 6)
355        check('2 + "$a.$b"', 5)
356        check('4*[llength "6 2"]', 8)
357        check('{word one} < "word $a"', 0)
358        check('4*2 < 7', 0)
359        check('hypot($a, 4)', 5)
360        check('5 / 4', 1)
361        check('5 / 4.0', 1)
362        check('5 / ( [string length "abcd"] + 0.0 )', 1)
363        check('20.0/5.0', 4)
364        check('"0x03" > "2"', 1)
365        check('[string length "a\xbd\u20ac"]', 3)
366        check(r'[string length "a\xbd\u20ac"]', 3)
367        self.assertRaises(TclError, tcl.exprlong, '"abc"')
368        self.assertRaises(TclError, tcl.exprlong, '2**64')
369
370    def test_exprboolean(self):
371        tcl = self.interp
372        tcl.call('set', 'a', 3)
373        tcl.call('set', 'b', 6)
374        def check(expr, expected):
375            result = tcl.exprboolean(expr)
376            self.assertEqual(result, expected)
377            self.assertIsInstance(result, int)
378            self.assertNotIsInstance(result, bool)
379
380        self.assertRaises(TypeError, tcl.exprboolean)
381        self.assertRaises(TypeError, tcl.exprboolean, '8.2', '+6')
382        self.assertRaises(TypeError, tcl.exprboolean, b'8.2 + 6')
383        self.assertRaises(TclError, tcl.exprboolean, 'spam')
384        check('', False)
385        for value in ('0', 'false', 'no', 'off'):
386            check(value, False)
387            check('"%s"' % value, False)
388            check('{%s}' % value, False)
389        for value in ('1', 'true', 'yes', 'on'):
390            check(value, True)
391            check('"%s"' % value, True)
392            check('{%s}' % value, True)
393        check('8.2 + 6', True)
394        check('3.1 + $a', True)
395        check('2 + "$a.$b"', True)
396        check('4*[llength "6 2"]', True)
397        check('{word one} < "word $a"', False)
398        check('4*2 < 7', False)
399        check('hypot($a, 4)', True)
400        check('5 / 4', True)
401        check('5 / 4.0', True)
402        check('5 / ( [string length "abcd"] + 0.0 )', True)
403        check('20.0/5.0', True)
404        check('"0x03" > "2"', True)
405        check('[string length "a\xbd\u20ac"]', True)
406        check(r'[string length "a\xbd\u20ac"]', True)
407        self.assertRaises(TclError, tcl.exprboolean, '"abc"')
408        check('2**64', True)
409
410    def test_booleans(self):
411        tcl = self.interp
412        def check(expr, expected):
413            result = tcl.call('expr', expr)
414            if tcl.wantobjects():
415                self.assertEqual(result, expected)
416                self.assertIsInstance(result, int)
417            else:
418                self.assertIn(result, (expr, str(int(expected))))
419                self.assertIsInstance(result, str)
420        check('true', True)
421        check('yes', True)
422        check('on', True)
423        check('false', False)
424        check('no', False)
425        check('off', False)
426        check('1 < 2', True)
427        check('1 > 2', False)
428
429    def test_expr_bignum(self):
430        tcl = self.interp
431        for i in self.get_integers():
432            result = tcl.call('expr', str(i))
433            if self.wantobjects:
434                self.assertEqual(result, i)
435                self.assertIsInstance(result, int)
436            else:
437                self.assertEqual(result, str(i))
438                self.assertIsInstance(result, str)
439
440    def test_passing_values(self):
441        def passValue(value):
442            return self.interp.call('set', '_', value)
443
444        self.assertEqual(passValue(True), True if self.wantobjects else '1')
445        self.assertEqual(passValue(False), False if self.wantobjects else '0')
446        self.assertEqual(passValue('string'), 'string')
447        self.assertEqual(passValue('string\u20ac'), 'string\u20ac')
448        self.assertEqual(passValue('string\U0001f4bb'), 'string\U0001f4bb')
449        self.assertEqual(passValue('str\x00ing'), 'str\x00ing')
450        self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd')
451        self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac')
452        self.assertEqual(passValue('str\x00ing\U0001f4bb'),
453                         'str\x00ing\U0001f4bb')
454        if sys.platform != 'win32':
455            self.assertEqual(passValue('<\udce2\udc82\udcac>'),
456                             '<\u20ac>')
457            self.assertEqual(passValue('<\udced\udca0\udcbd\udced\udcb2\udcbb>'),
458                             '<\U0001f4bb>')
459        self.assertEqual(passValue(b'str\x00ing'),
460                         b'str\x00ing' if self.wantobjects else 'str\x00ing')
461        self.assertEqual(passValue(b'str\xc0\x80ing'),
462                         b'str\xc0\x80ing' if self.wantobjects else 'str\xc0\x80ing')
463        self.assertEqual(passValue(b'str\xbding'),
464                         b'str\xbding' if self.wantobjects else 'str\xbding')
465        for i in self.get_integers():
466            self.assertEqual(passValue(i), i if self.wantobjects else str(i))
467        for f in (0.0, 1.0, -1.0, 1/3,
468                  sys.float_info.min, sys.float_info.max,
469                  -sys.float_info.min, -sys.float_info.max):
470            if self.wantobjects:
471                self.assertEqual(passValue(f), f)
472            else:
473                self.assertEqual(float(passValue(f)), f)
474        if self.wantobjects:
475            f = passValue(float('nan'))
476            self.assertNotEqual(f, f)
477            self.assertEqual(passValue(float('inf')), float('inf'))
478            self.assertEqual(passValue(-float('inf')), -float('inf'))
479        else:
480            self.assertEqual(float(passValue(float('inf'))), float('inf'))
481            self.assertEqual(float(passValue(-float('inf'))), -float('inf'))
482            # XXX NaN representation can be not parsable by float()
483        self.assertEqual(passValue((1, '2', (3.4,))),
484                         (1, '2', (3.4,)) if self.wantobjects else '1 2 3.4')
485        self.assertEqual(passValue(['a', ['b', 'c']]),
486                         ('a', ('b', 'c')) if self.wantobjects else 'a {b c}')
487
488    def test_user_command(self):
489        result = None
490        def testfunc(arg):
491            nonlocal result
492            result = arg
493            return arg
494        self.interp.createcommand('testfunc', testfunc)
495        self.addCleanup(self.interp.tk.deletecommand, 'testfunc')
496        def check(value, expected=None, *, eq=self.assertEqual):
497            if expected is None:
498                expected = value
499            nonlocal result
500            result = None
501            r = self.interp.call('testfunc', value)
502            self.assertIsInstance(result, str)
503            eq(result, expected)
504            self.assertIsInstance(r, str)
505            eq(r, expected)
506        def float_eq(actual, expected):
507            self.assertAlmostEqual(float(actual), expected,
508                                   delta=abs(expected) * 1e-10)
509
510        check(True, '1')
511        check(False, '0')
512        check('string')
513        check('string\xbd')
514        check('string\u20ac')
515        check('string\U0001f4bb')
516        if sys.platform != 'win32':
517            check('<\udce2\udc82\udcac>', '<\u20ac>')
518            check('<\udced\udca0\udcbd\udced\udcb2\udcbb>', '<\U0001f4bb>')
519        check('')
520        check(b'string', 'string')
521        check(b'string\xe2\x82\xac', 'string\xe2\x82\xac')
522        check(b'string\xbd', 'string\xbd')
523        check(b'', '')
524        check('str\x00ing')
525        check('str\x00ing\xbd')
526        check('str\x00ing\u20ac')
527        check(b'str\x00ing', 'str\x00ing')
528        check(b'str\xc0\x80ing', 'str\xc0\x80ing')
529        check(b'str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac')
530        for i in self.get_integers():
531            check(i, str(i))
532        for f in (0.0, 1.0, -1.0):
533            check(f, repr(f))
534        for f in (1/3.0, sys.float_info.min, sys.float_info.max,
535                  -sys.float_info.min, -sys.float_info.max):
536            check(f, eq=float_eq)
537        check(float('inf'), eq=float_eq)
538        check(-float('inf'), eq=float_eq)
539        # XXX NaN representation can be not parsable by float()
540        check((), '')
541        check((1, (2,), (3, 4), '5 6', ()), '1 2 {3 4} {5 6} {}')
542        check([1, [2,], [3, 4], '5 6', []], '1 2 {3 4} {5 6} {}')
543
544    def test_splitlist(self):
545        splitlist = self.interp.tk.splitlist
546        call = self.interp.tk.call
547        self.assertRaises(TypeError, splitlist)
548        self.assertRaises(TypeError, splitlist, 'a', 'b')
549        self.assertRaises(TypeError, splitlist, 2)
550        testcases = [
551            ('2', ('2',)),
552            ('', ()),
553            ('{}', ('',)),
554            ('""', ('',)),
555            ('a\n b\t\r c\n ', ('a', 'b', 'c')),
556            (b'a\n b\t\r c\n ', ('a', 'b', 'c')),
557            ('a \u20ac', ('a', '\u20ac')),
558            ('a \U0001f4bb', ('a', '\U0001f4bb')),
559            (b'a \xe2\x82\xac', ('a', '\u20ac')),
560            (b'a \xf0\x9f\x92\xbb', ('a', '\U0001f4bb')),
561            (b'a \xed\xa0\xbd\xed\xb2\xbb', ('a', '\U0001f4bb')),
562            (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
563            ('a {b c}', ('a', 'b c')),
564            (r'a b\ c', ('a', 'b c')),
565            (('a', 'b c'), ('a', 'b c')),
566            ('a 2', ('a', '2')),
567            (('a', 2), ('a', 2)),
568            ('a 3.4', ('a', '3.4')),
569            (('a', 3.4), ('a', 3.4)),
570            ((), ()),
571            ([], ()),
572            (['a', ['b', 'c']], ('a', ['b', 'c'])),
573            (call('list', 1, '2', (3.4,)),
574                (1, '2', (3.4,)) if self.wantobjects else
575                ('1', '2', '3.4')),
576        ]
577        tk_patchlevel = get_tk_patchlevel()
578        if not self.wantobjects:
579            expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4')
580        else:
581            expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,))
582        testcases += [
583            (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)),
584                expected),
585        ]
586        dbg_info = ('want objects? %s, Tcl version: %s, Tk patchlevel: %s'
587                    % (self.wantobjects, tcl_version, tk_patchlevel))
588        for arg, res in testcases:
589            self.assertEqual(splitlist(arg), res,
590                             'arg=%a, %s' % (arg, dbg_info))
591        self.assertRaises(TclError, splitlist, '{')
592
593    def test_splitdict(self):
594        splitdict = tkinter._splitdict
595        tcl = self.interp.tk
596
597        arg = '-a {1 2 3} -something foo status {}'
598        self.assertEqual(splitdict(tcl, arg, False),
599            {'-a': '1 2 3', '-something': 'foo', 'status': ''})
600        self.assertEqual(splitdict(tcl, arg),
601            {'a': '1 2 3', 'something': 'foo', 'status': ''})
602
603        arg = ('-a', (1, 2, 3), '-something', 'foo', 'status', '{}')
604        self.assertEqual(splitdict(tcl, arg, False),
605            {'-a': (1, 2, 3), '-something': 'foo', 'status': '{}'})
606        self.assertEqual(splitdict(tcl, arg),
607            {'a': (1, 2, 3), 'something': 'foo', 'status': '{}'})
608
609        self.assertRaises(RuntimeError, splitdict, tcl, '-a b -c ')
610        self.assertRaises(RuntimeError, splitdict, tcl, ('-a', 'b', '-c'))
611
612        arg = tcl.call('list',
613                        '-a', (1, 2, 3), '-something', 'foo', 'status', ())
614        self.assertEqual(splitdict(tcl, arg),
615            {'a': (1, 2, 3) if self.wantobjects else '1 2 3',
616             'something': 'foo', 'status': ''})
617
618        arg = tcl.call('dict', 'create',
619                       '-a', (1, 2, 3), '-something', 'foo', 'status', ())
620        if not self.wantobjects:
621            expected = {'a': '1 2 3', 'something': 'foo', 'status': ''}
622        else:
623            expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''}
624        self.assertEqual(splitdict(tcl, arg), expected)
625
626    def test_join(self):
627        join = tkinter._join
628        tcl = self.interp.tk
629        def unpack(s):
630            return tcl.call('lindex', s, 0)
631        def check(value):
632            self.assertEqual(unpack(join([value])), value)
633            self.assertEqual(unpack(join([value, 0])), value)
634            self.assertEqual(unpack(unpack(join([[value]]))), value)
635            self.assertEqual(unpack(unpack(join([[value, 0]]))), value)
636            self.assertEqual(unpack(unpack(join([[value], 0]))), value)
637            self.assertEqual(unpack(unpack(join([[value, 0], 0]))), value)
638        check('')
639        check('spam')
640        check('sp am')
641        check('sp\tam')
642        check('sp\nam')
643        check(' \t\n')
644        check('{spam}')
645        check('{sp am}')
646        check('"spam"')
647        check('"sp am"')
648        check('{"spam"}')
649        check('"{spam}"')
650        check('sp\\am')
651        check('"sp\\am"')
652        check('"{}" "{}"')
653        check('"\\')
654        check('"{')
655        check('"}')
656        check('\n\\')
657        check('\n{')
658        check('\n}')
659        check('\\\n')
660        check('{\n')
661        check('}\n')
662
663    @support.cpython_only
664    def test_new_tcl_obj(self):
665        support.check_disallow_instantiation(self, _tkinter.Tcl_Obj)
666        support.check_disallow_instantiation(self, _tkinter.TkttType)
667        support.check_disallow_instantiation(self, _tkinter.TkappType)
668
669class BigmemTclTest(unittest.TestCase):
670
671    def setUp(self):
672        self.interp = Tcl()
673
674    @support.cpython_only
675    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
676    @support.bigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False)
677    def test_huge_string_call(self, size):
678        value = ' ' * size
679        self.assertRaises(OverflowError, self.interp.call, 'string', 'index', value, 0)
680
681    @support.cpython_only
682    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
683    @support.bigmemtest(size=INT_MAX + 1, memuse=2, dry_run=False)
684    def test_huge_string_builtins(self, size):
685        tk = self.interp.tk
686        value = '1' + ' ' * size
687        self.assertRaises(OverflowError, tk.getint, value)
688        self.assertRaises(OverflowError, tk.getdouble, value)
689        self.assertRaises(OverflowError, tk.getboolean, value)
690        self.assertRaises(OverflowError, tk.eval, value)
691        self.assertRaises(OverflowError, tk.evalfile, value)
692        self.assertRaises(OverflowError, tk.record, value)
693        self.assertRaises(OverflowError, tk.adderrorinfo, value)
694        self.assertRaises(OverflowError, tk.setvar, value, 'x', 'a')
695        self.assertRaises(OverflowError, tk.setvar, 'x', value, 'a')
696        self.assertRaises(OverflowError, tk.unsetvar, value)
697        self.assertRaises(OverflowError, tk.unsetvar, 'x', value)
698        self.assertRaises(OverflowError, tk.adderrorinfo, value)
699        self.assertRaises(OverflowError, tk.exprstring, value)
700        self.assertRaises(OverflowError, tk.exprlong, value)
701        self.assertRaises(OverflowError, tk.exprboolean, value)
702        self.assertRaises(OverflowError, tk.splitlist, value)
703        self.assertRaises(OverflowError, tk.createcommand, value, max)
704        self.assertRaises(OverflowError, tk.deletecommand, value)
705
706    @support.cpython_only
707    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
708    @support.bigmemtest(size=INT_MAX + 1, memuse=6, dry_run=False)
709    def test_huge_string_builtins2(self, size):
710        # These commands require larger memory for possible error messages
711        tk = self.interp.tk
712        value = '1' + ' ' * size
713        self.assertRaises(OverflowError, tk.evalfile, value)
714        self.assertRaises(OverflowError, tk.unsetvar, value)
715        self.assertRaises(OverflowError, tk.unsetvar, 'x', value)
716
717
718def setUpModule():
719    if support.verbose:
720        tcl = Tcl()
721        print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True)
722
723
724if __name__ == "__main__":
725    unittest.main()
726