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