• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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('string\U0001f4bb'), 'string\U0001f4bb')
433        self.assertEqual(passValue('str\x00ing'), 'str\x00ing')
434        self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd')
435        self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac')
436        self.assertEqual(passValue('str\x00ing\U0001f4bb'),
437                         'str\x00ing\U0001f4bb')
438        self.assertEqual(passValue(b'str\x00ing'),
439                         b'str\x00ing' if self.wantobjects else 'str\x00ing')
440        self.assertEqual(passValue(b'str\xc0\x80ing'),
441                         b'str\xc0\x80ing' if self.wantobjects else 'str\xc0\x80ing')
442        self.assertEqual(passValue(b'str\xbding'),
443                         b'str\xbding' if self.wantobjects else 'str\xbding')
444        for i in self.get_integers():
445            self.assertEqual(passValue(i), i if self.wantobjects else str(i))
446        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
447            self.assertEqual(passValue(2**1000), str(2**1000))
448        for f in (0.0, 1.0, -1.0, 1/3,
449                  sys.float_info.min, sys.float_info.max,
450                  -sys.float_info.min, -sys.float_info.max):
451            if self.wantobjects:
452                self.assertEqual(passValue(f), f)
453            else:
454                self.assertEqual(float(passValue(f)), f)
455        if self.wantobjects:
456            f = passValue(float('nan'))
457            self.assertNotEqual(f, f)
458            self.assertEqual(passValue(float('inf')), float('inf'))
459            self.assertEqual(passValue(-float('inf')), -float('inf'))
460        else:
461            self.assertEqual(float(passValue(float('inf'))), float('inf'))
462            self.assertEqual(float(passValue(-float('inf'))), -float('inf'))
463            # XXX NaN representation can be not parsable by float()
464        self.assertEqual(passValue((1, '2', (3.4,))),
465                         (1, '2', (3.4,)) if self.wantobjects else '1 2 3.4')
466        self.assertEqual(passValue(['a', ['b', 'c']]),
467                         ('a', ('b', 'c')) if self.wantobjects else 'a {b c}')
468
469    def test_user_command(self):
470        result = None
471        def testfunc(arg):
472            nonlocal result
473            result = arg
474            return arg
475        self.interp.createcommand('testfunc', testfunc)
476        self.addCleanup(self.interp.tk.deletecommand, 'testfunc')
477        def check(value, expected=None, *, eq=self.assertEqual):
478            if expected is None:
479                expected = value
480            nonlocal result
481            result = None
482            r = self.interp.call('testfunc', value)
483            self.assertIsInstance(result, str)
484            eq(result, expected)
485            self.assertIsInstance(r, str)
486            eq(r, expected)
487        def float_eq(actual, expected):
488            self.assertAlmostEqual(float(actual), expected,
489                                   delta=abs(expected) * 1e-10)
490
491        check(True, '1')
492        check(False, '0')
493        check('string')
494        check('string\xbd')
495        check('string\u20ac')
496        check('string\U0001f4bb')
497        check('')
498        check(b'string', 'string')
499        check(b'string\xe2\x82\xac', 'string\xe2\x82\xac')
500        check(b'string\xbd', 'string\xbd')
501        check(b'', '')
502        check('str\x00ing')
503        check('str\x00ing\xbd')
504        check('str\x00ing\u20ac')
505        check(b'str\x00ing', 'str\x00ing')
506        check(b'str\xc0\x80ing', 'str\xc0\x80ing')
507        check(b'str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac')
508        for i in self.get_integers():
509            check(i, str(i))
510        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
511            check(2**1000, str(2**1000))
512        for f in (0.0, 1.0, -1.0):
513            check(f, repr(f))
514        for f in (1/3.0, sys.float_info.min, sys.float_info.max,
515                  -sys.float_info.min, -sys.float_info.max):
516            check(f, eq=float_eq)
517        check(float('inf'), eq=float_eq)
518        check(-float('inf'), eq=float_eq)
519        # XXX NaN representation can be not parsable by float()
520        check((), '')
521        check((1, (2,), (3, 4), '5 6', ()), '1 2 {3 4} {5 6} {}')
522        check([1, [2,], [3, 4], '5 6', []], '1 2 {3 4} {5 6} {}')
523
524    def test_splitlist(self):
525        splitlist = self.interp.tk.splitlist
526        call = self.interp.tk.call
527        self.assertRaises(TypeError, splitlist)
528        self.assertRaises(TypeError, splitlist, 'a', 'b')
529        self.assertRaises(TypeError, splitlist, 2)
530        testcases = [
531            ('2', ('2',)),
532            ('', ()),
533            ('{}', ('',)),
534            ('""', ('',)),
535            ('a\n b\t\r c\n ', ('a', 'b', 'c')),
536            (b'a\n b\t\r c\n ', ('a', 'b', 'c')),
537            ('a \u20ac', ('a', '\u20ac')),
538            ('a \U0001f4bb', ('a', '\U0001f4bb')),
539            (b'a \xe2\x82\xac', ('a', '\u20ac')),
540            (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
541            ('a {b c}', ('a', 'b c')),
542            (r'a b\ c', ('a', 'b c')),
543            (('a', 'b c'), ('a', 'b c')),
544            ('a 2', ('a', '2')),
545            (('a', 2), ('a', 2)),
546            ('a 3.4', ('a', '3.4')),
547            (('a', 3.4), ('a', 3.4)),
548            ((), ()),
549            ([], ()),
550            (['a', ['b', 'c']], ('a', ['b', 'c'])),
551            (call('list', 1, '2', (3.4,)),
552                (1, '2', (3.4,)) if self.wantobjects else
553                ('1', '2', '3.4')),
554        ]
555        tk_patchlevel = get_tk_patchlevel()
556        if tcl_version >= (8, 5):
557            if not self.wantobjects or tk_patchlevel < (8, 5, 5):
558                # Before 8.5.5 dicts were converted to lists through string
559                expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4')
560            else:
561                expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,))
562            testcases += [
563                (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)),
564                    expected),
565            ]
566        dbg_info = ('want objects? %s, Tcl version: %s, Tk patchlevel: %s'
567                    % (self.wantobjects, tcl_version, tk_patchlevel))
568        for arg, res in testcases:
569            self.assertEqual(splitlist(arg), res,
570                             'arg=%a, %s' % (arg, dbg_info))
571        self.assertRaises(TclError, splitlist, '{')
572
573    def test_split(self):
574        split = self.interp.tk.split
575        call = self.interp.tk.call
576        self.assertRaises(TypeError, split)
577        self.assertRaises(TypeError, split, 'a', 'b')
578        self.assertRaises(TypeError, split, 2)
579        testcases = [
580            ('2', '2'),
581            ('', ''),
582            ('{}', ''),
583            ('""', ''),
584            ('{', '{'),
585            ('a\n b\t\r c\n ', ('a', 'b', 'c')),
586            (b'a\n b\t\r c\n ', ('a', 'b', 'c')),
587            ('a \u20ac', ('a', '\u20ac')),
588            (b'a \xe2\x82\xac', ('a', '\u20ac')),
589            (b'a\xc0\x80b', 'a\x00b'),
590            (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
591            (b'{a\xc0\x80b c\xc0\x80d', '{a\x00b c\x00d'),
592            ('a {b c}', ('a', ('b', 'c'))),
593            (r'a b\ c', ('a', ('b', 'c'))),
594            (('a', b'b c'), ('a', ('b', 'c'))),
595            (('a', 'b c'), ('a', ('b', 'c'))),
596            ('a 2', ('a', '2')),
597            (('a', 2), ('a', 2)),
598            ('a 3.4', ('a', '3.4')),
599            (('a', 3.4), ('a', 3.4)),
600            (('a', (2, 3.4)), ('a', (2, 3.4))),
601            ((), ()),
602            ([], ()),
603            (['a', 'b c'], ('a', ('b', 'c'))),
604            (['a', ['b', 'c']], ('a', ('b', 'c'))),
605            (call('list', 1, '2', (3.4,)),
606                (1, '2', (3.4,)) if self.wantobjects else
607                ('1', '2', '3.4')),
608        ]
609        if tcl_version >= (8, 5):
610            if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5):
611                # Before 8.5.5 dicts were converted to lists through string
612                expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4')
613            else:
614                expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,))
615            testcases += [
616                (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)),
617                    expected),
618            ]
619        for arg, res in testcases:
620            self.assertEqual(split(arg), res, msg=arg)
621
622    def test_splitdict(self):
623        splitdict = tkinter._splitdict
624        tcl = self.interp.tk
625
626        arg = '-a {1 2 3} -something foo status {}'
627        self.assertEqual(splitdict(tcl, arg, False),
628            {'-a': '1 2 3', '-something': 'foo', 'status': ''})
629        self.assertEqual(splitdict(tcl, arg),
630            {'a': '1 2 3', 'something': 'foo', 'status': ''})
631
632        arg = ('-a', (1, 2, 3), '-something', 'foo', 'status', '{}')
633        self.assertEqual(splitdict(tcl, arg, False),
634            {'-a': (1, 2, 3), '-something': 'foo', 'status': '{}'})
635        self.assertEqual(splitdict(tcl, arg),
636            {'a': (1, 2, 3), 'something': 'foo', 'status': '{}'})
637
638        self.assertRaises(RuntimeError, splitdict, tcl, '-a b -c ')
639        self.assertRaises(RuntimeError, splitdict, tcl, ('-a', 'b', '-c'))
640
641        arg = tcl.call('list',
642                        '-a', (1, 2, 3), '-something', 'foo', 'status', ())
643        self.assertEqual(splitdict(tcl, arg),
644            {'a': (1, 2, 3) if self.wantobjects else '1 2 3',
645             'something': 'foo', 'status': ''})
646
647        if tcl_version >= (8, 5):
648            arg = tcl.call('dict', 'create',
649                           '-a', (1, 2, 3), '-something', 'foo', 'status', ())
650            if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5):
651                # Before 8.5.5 dicts were converted to lists through string
652                expected = {'a': '1 2 3', 'something': 'foo', 'status': ''}
653            else:
654                expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''}
655            self.assertEqual(splitdict(tcl, arg), expected)
656
657    def test_join(self):
658        join = tkinter._join
659        tcl = self.interp.tk
660        def unpack(s):
661            return tcl.call('lindex', s, 0)
662        def check(value):
663            self.assertEqual(unpack(join([value])), value)
664            self.assertEqual(unpack(join([value, 0])), value)
665            self.assertEqual(unpack(unpack(join([[value]]))), value)
666            self.assertEqual(unpack(unpack(join([[value, 0]]))), value)
667            self.assertEqual(unpack(unpack(join([[value], 0]))), value)
668            self.assertEqual(unpack(unpack(join([[value, 0], 0]))), value)
669        check('')
670        check('spam')
671        check('sp am')
672        check('sp\tam')
673        check('sp\nam')
674        check(' \t\n')
675        check('{spam}')
676        check('{sp am}')
677        check('"spam"')
678        check('"sp am"')
679        check('{"spam"}')
680        check('"{spam}"')
681        check('sp\\am')
682        check('"sp\\am"')
683        check('"{}" "{}"')
684        check('"\\')
685        check('"{')
686        check('"}')
687        check('\n\\')
688        check('\n{')
689        check('\n}')
690        check('\\\n')
691        check('{\n')
692        check('}\n')
693
694    def test_new_tcl_obj(self):
695        self.assertRaises(TypeError, _tkinter.Tcl_Obj)
696
697class BigmemTclTest(unittest.TestCase):
698
699    def setUp(self):
700        self.interp = Tcl()
701
702    @support.cpython_only
703    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
704    @support.bigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False)
705    def test_huge_string_call(self, size):
706        value = ' ' * size
707        self.assertRaises(OverflowError, self.interp.call, 'string', 'index', value, 0)
708
709    @support.cpython_only
710    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
711    @support.bigmemtest(size=INT_MAX + 1, memuse=2, dry_run=False)
712    def test_huge_string_builtins(self, size):
713        tk = self.interp.tk
714        value = '1' + ' ' * size
715        self.assertRaises(OverflowError, tk.getint, value)
716        self.assertRaises(OverflowError, tk.getdouble, value)
717        self.assertRaises(OverflowError, tk.getboolean, value)
718        self.assertRaises(OverflowError, tk.eval, value)
719        self.assertRaises(OverflowError, tk.evalfile, value)
720        self.assertRaises(OverflowError, tk.record, value)
721        self.assertRaises(OverflowError, tk.adderrorinfo, value)
722        self.assertRaises(OverflowError, tk.setvar, value, 'x', 'a')
723        self.assertRaises(OverflowError, tk.setvar, 'x', value, 'a')
724        self.assertRaises(OverflowError, tk.unsetvar, value)
725        self.assertRaises(OverflowError, tk.unsetvar, 'x', value)
726        self.assertRaises(OverflowError, tk.adderrorinfo, value)
727        self.assertRaises(OverflowError, tk.exprstring, value)
728        self.assertRaises(OverflowError, tk.exprlong, value)
729        self.assertRaises(OverflowError, tk.exprboolean, value)
730        self.assertRaises(OverflowError, tk.splitlist, value)
731        self.assertRaises(OverflowError, tk.split, value)
732        self.assertRaises(OverflowError, tk.createcommand, value, max)
733        self.assertRaises(OverflowError, tk.deletecommand, value)
734
735    @support.cpython_only
736    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
737    @support.bigmemtest(size=INT_MAX + 1, memuse=6, dry_run=False)
738    def test_huge_string_builtins2(self, size):
739        # These commands require larger memory for possible error messages
740        tk = self.interp.tk
741        value = '1' + ' ' * size
742        self.assertRaises(OverflowError, tk.evalfile, value)
743        self.assertRaises(OverflowError, tk.unsetvar, value)
744        self.assertRaises(OverflowError, tk.unsetvar, 'x', value)
745
746
747def setUpModule():
748    if support.verbose:
749        tcl = Tcl()
750        print('patchlevel =', tcl.call('info', 'patchlevel'))
751
752
753def test_main():
754    support.run_unittest(TclTest, TkinterTest, BigmemTclTest)
755
756if __name__ == "__main__":
757    test_main()
758