• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import locale
3import re
4import subprocess
5import sys
6import os
7import warnings
8from test import support
9
10# Skip this test if the _tkinter module wasn't built.
11_tkinter = support.import_module('_tkinter')
12
13import tkinter
14from tkinter import Tcl
15from _tkinter import TclError
16
17try:
18    from _testcapi import INT_MAX, PY_SSIZE_T_MAX
19except ImportError:
20    INT_MAX = PY_SSIZE_T_MAX = sys.maxsize
21
22tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.')))
23
24_tk_patchlevel = None
25def get_tk_patchlevel():
26    global _tk_patchlevel
27    if _tk_patchlevel is None:
28        tcl = Tcl()
29        patchlevel = tcl.call('info', 'patchlevel')
30        m = re.fullmatch(r'(\d+)\.(\d+)([ab.])(\d+)', patchlevel)
31        major, minor, releaselevel, serial = m.groups()
32        major, minor, serial = int(major), int(minor), int(serial)
33        releaselevel = {'a': 'alpha', 'b': 'beta', '.': 'final'}[releaselevel]
34        if releaselevel == 'final':
35            _tk_patchlevel = major, minor, serial, releaselevel, 0
36        else:
37            _tk_patchlevel = major, minor, 0, releaselevel, serial
38    return _tk_patchlevel
39
40
41class TkinterTest(unittest.TestCase):
42
43    def testFlattenLen(self):
44        # flatten(<object with no length>)
45        self.assertRaises(TypeError, _tkinter._flatten, True)
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        integers = (0, 1, -1, 2**31-1, -2**31, 2**31, -2**31-1, 2**63-1, -2**63)
139        # bignum was added in Tcl 8.5, but its support is able only since 8.5.8.
140        # Actually it is determined at compile time, so using get_tk_patchlevel()
141        # is not reliable.
142        # TODO: expose full static version.
143        if tcl_version >= (8, 5):
144            v = get_tk_patchlevel()
145            if v >= (8, 6, 0, 'final') or (8, 5, 8) <= v < (8, 6):
146                integers += (2**63, -2**63-1, 2**1000, -2**1000)
147        return integers
148
149    def test_getint(self):
150        tcl = self.interp.tk
151        for i in self.get_integers():
152            self.assertEqual(tcl.getint(' %d ' % i), i)
153            if tcl_version >= (8, 5):
154                self.assertEqual(tcl.getint(' %#o ' % i), i)
155            self.assertEqual(tcl.getint((' %#o ' % i).replace('o', '')), i)
156            self.assertEqual(tcl.getint(' %#x ' % i), i)
157        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
158            self.assertRaises(TclError, tcl.getint, str(2**1000))
159        self.assertEqual(tcl.getint(42), 42)
160        self.assertRaises(TypeError, tcl.getint)
161        self.assertRaises(TypeError, tcl.getint, '42', '10')
162        self.assertRaises(TypeError, tcl.getint, b'42')
163        self.assertRaises(TypeError, tcl.getint, 42.0)
164        self.assertRaises(TclError, tcl.getint, 'a')
165        self.assertRaises((TypeError, ValueError, TclError),
166                          tcl.getint, '42\0')
167        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
168                          tcl.getint, '42\ud800')
169
170    def test_getdouble(self):
171        tcl = self.interp.tk
172        self.assertEqual(tcl.getdouble(' 42 '), 42.0)
173        self.assertEqual(tcl.getdouble(' 42.5 '), 42.5)
174        self.assertEqual(tcl.getdouble(42.5), 42.5)
175        self.assertEqual(tcl.getdouble(42), 42.0)
176        self.assertRaises(TypeError, tcl.getdouble)
177        self.assertRaises(TypeError, tcl.getdouble, '42.5', '10')
178        self.assertRaises(TypeError, tcl.getdouble, b'42.5')
179        self.assertRaises(TclError, tcl.getdouble, 'a')
180        self.assertRaises((TypeError, ValueError, TclError),
181                          tcl.getdouble, '42.5\0')
182        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
183                          tcl.getdouble, '42.5\ud800')
184
185    def test_getboolean(self):
186        tcl = self.interp.tk
187        self.assertIs(tcl.getboolean('on'), True)
188        self.assertIs(tcl.getboolean('1'), True)
189        self.assertIs(tcl.getboolean(42), True)
190        self.assertIs(tcl.getboolean(0), False)
191        self.assertRaises(TypeError, tcl.getboolean)
192        self.assertRaises(TypeError, tcl.getboolean, 'on', '1')
193        self.assertRaises(TypeError, tcl.getboolean, b'on')
194        self.assertRaises(TypeError, tcl.getboolean, 1.0)
195        self.assertRaises(TclError, tcl.getboolean, 'a')
196        self.assertRaises((TypeError, ValueError, TclError),
197                          tcl.getboolean, 'on\0')
198        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
199                          tcl.getboolean, 'on\ud800')
200
201    def testEvalFile(self):
202        tcl = self.interp
203        filename = support.TESTFN_ASCII
204        self.addCleanup(support.unlink, filename)
205        with open(filename, 'w') as f:
206            f.write("""set a 1
207            set b 2
208            set c [ expr $a + $b ]
209            """)
210        tcl.evalfile(filename)
211        self.assertEqual(tcl.eval('set a'),'1')
212        self.assertEqual(tcl.eval('set b'),'2')
213        self.assertEqual(tcl.eval('set c'),'3')
214
215    def test_evalfile_null_in_result(self):
216        tcl = self.interp
217        filename = support.TESTFN_ASCII
218        self.addCleanup(support.unlink, filename)
219        with open(filename, 'w') as f:
220            f.write("""
221            set a "a\0b"
222            set b "a\\0b"
223            """)
224        tcl.evalfile(filename)
225        self.assertEqual(tcl.eval('set a'), 'a\x00b')
226        self.assertEqual(tcl.eval('set b'), 'a\x00b')
227
228    def test_evalfile_surrogates_in_result(self):
229        tcl = self.interp
230        encoding = tcl.call('encoding', 'system')
231        self.addCleanup(tcl.call, 'encoding', 'system', encoding)
232        tcl.call('encoding', 'system', 'utf-8')
233
234        filename = support.TESTFN_ASCII
235        self.addCleanup(support.unlink, filename)
236        with open(filename, 'wb') as f:
237            f.write(b"""
238            set a "<\xed\xa0\xbd\xed\xb2\xbb>"
239            set b "<\\ud83d\\udcbb>"
240            """)
241        tcl.evalfile(filename)
242        self.assertEqual(tcl.eval('set a'), '<\U0001f4bb>')
243        self.assertEqual(tcl.eval('set b'), '<\U0001f4bb>')
244
245    def testEvalFileException(self):
246        tcl = self.interp
247        filename = "doesnotexists"
248        try:
249            os.remove(filename)
250        except Exception as e:
251            pass
252        self.assertRaises(TclError,tcl.evalfile,filename)
253
254    def testPackageRequireException(self):
255        tcl = self.interp
256        self.assertRaises(TclError,tcl.eval,'package require DNE')
257
258    @unittest.skipUnless(sys.platform == 'win32', 'Requires Windows')
259    def testLoadWithUNC(self):
260        # Build a UNC path from the regular path.
261        # Something like
262        #   \\%COMPUTERNAME%\c$\python27\python.exe
263
264        fullname = os.path.abspath(sys.executable)
265        if fullname[1] != ':':
266            raise unittest.SkipTest('Absolute path should have drive part')
267        unc_name = r'\\%s\%s$\%s' % (os.environ['COMPUTERNAME'],
268                                    fullname[0],
269                                    fullname[3:])
270        if not os.path.exists(unc_name):
271            raise unittest.SkipTest('Cannot connect to UNC Path')
272
273        with support.EnvironmentVarGuard() as env:
274            env.unset("TCL_LIBRARY")
275            stdout = subprocess.check_output(
276                    [unc_name, '-c', 'import tkinter; print(tkinter)'])
277
278        self.assertIn(b'tkinter', stdout)
279
280    def test_exprstring(self):
281        tcl = self.interp
282        tcl.call('set', 'a', 3)
283        tcl.call('set', 'b', 6)
284        def check(expr, expected):
285            result = tcl.exprstring(expr)
286            self.assertEqual(result, expected)
287            self.assertIsInstance(result, str)
288
289        self.assertRaises(TypeError, tcl.exprstring)
290        self.assertRaises(TypeError, tcl.exprstring, '8.2', '+6')
291        self.assertRaises(TypeError, tcl.exprstring, b'8.2 + 6')
292        self.assertRaises(TclError, tcl.exprstring, 'spam')
293        check('', '0')
294        check('8.2 + 6', '14.2')
295        check('3.1 + $a', '6.1')
296        check('2 + "$a.$b"', '5.6')
297        check('4*[llength "6 2"]', '8')
298        check('{word one} < "word $a"', '0')
299        check('4*2 < 7', '0')
300        check('hypot($a, 4)', '5.0')
301        check('5 / 4', '1')
302        check('5 / 4.0', '1.25')
303        check('5 / ( [string length "abcd"] + 0.0 )', '1.25')
304        check('20.0/5.0', '4.0')
305        check('"0x03" > "2"', '1')
306        check('[string length "a\xbd\u20ac"]', '3')
307        check(r'[string length "a\xbd\u20ac"]', '3')
308        check('"abc"', 'abc')
309        check('"a\xbd\u20ac"', 'a\xbd\u20ac')
310        check(r'"a\xbd\u20ac"', 'a\xbd\u20ac')
311        check(r'"a\0b"', 'a\x00b')
312        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
313            check('2**64', str(2**64))
314
315    def test_exprdouble(self):
316        tcl = self.interp
317        tcl.call('set', 'a', 3)
318        tcl.call('set', 'b', 6)
319        def check(expr, expected):
320            result = tcl.exprdouble(expr)
321            self.assertEqual(result, expected)
322            self.assertIsInstance(result, float)
323
324        self.assertRaises(TypeError, tcl.exprdouble)
325        self.assertRaises(TypeError, tcl.exprdouble, '8.2', '+6')
326        self.assertRaises(TypeError, tcl.exprdouble, b'8.2 + 6')
327        self.assertRaises(TclError, tcl.exprdouble, 'spam')
328        check('', 0.0)
329        check('8.2 + 6', 14.2)
330        check('3.1 + $a', 6.1)
331        check('2 + "$a.$b"', 5.6)
332        check('4*[llength "6 2"]', 8.0)
333        check('{word one} < "word $a"', 0.0)
334        check('4*2 < 7', 0.0)
335        check('hypot($a, 4)', 5.0)
336        check('5 / 4', 1.0)
337        check('5 / 4.0', 1.25)
338        check('5 / ( [string length "abcd"] + 0.0 )', 1.25)
339        check('20.0/5.0', 4.0)
340        check('"0x03" > "2"', 1.0)
341        check('[string length "a\xbd\u20ac"]', 3.0)
342        check(r'[string length "a\xbd\u20ac"]', 3.0)
343        self.assertRaises(TclError, tcl.exprdouble, '"abc"')
344        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
345            check('2**64', float(2**64))
346
347    def test_exprlong(self):
348        tcl = self.interp
349        tcl.call('set', 'a', 3)
350        tcl.call('set', 'b', 6)
351        def check(expr, expected):
352            result = tcl.exprlong(expr)
353            self.assertEqual(result, expected)
354            self.assertIsInstance(result, int)
355
356        self.assertRaises(TypeError, tcl.exprlong)
357        self.assertRaises(TypeError, tcl.exprlong, '8.2', '+6')
358        self.assertRaises(TypeError, tcl.exprlong, b'8.2 + 6')
359        self.assertRaises(TclError, tcl.exprlong, 'spam')
360        check('', 0)
361        check('8.2 + 6', 14)
362        check('3.1 + $a', 6)
363        check('2 + "$a.$b"', 5)
364        check('4*[llength "6 2"]', 8)
365        check('{word one} < "word $a"', 0)
366        check('4*2 < 7', 0)
367        check('hypot($a, 4)', 5)
368        check('5 / 4', 1)
369        check('5 / 4.0', 1)
370        check('5 / ( [string length "abcd"] + 0.0 )', 1)
371        check('20.0/5.0', 4)
372        check('"0x03" > "2"', 1)
373        check('[string length "a\xbd\u20ac"]', 3)
374        check(r'[string length "a\xbd\u20ac"]', 3)
375        self.assertRaises(TclError, tcl.exprlong, '"abc"')
376        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
377            self.assertRaises(TclError, tcl.exprlong, '2**64')
378
379    def test_exprboolean(self):
380        tcl = self.interp
381        tcl.call('set', 'a', 3)
382        tcl.call('set', 'b', 6)
383        def check(expr, expected):
384            result = tcl.exprboolean(expr)
385            self.assertEqual(result, expected)
386            self.assertIsInstance(result, int)
387            self.assertNotIsInstance(result, bool)
388
389        self.assertRaises(TypeError, tcl.exprboolean)
390        self.assertRaises(TypeError, tcl.exprboolean, '8.2', '+6')
391        self.assertRaises(TypeError, tcl.exprboolean, b'8.2 + 6')
392        self.assertRaises(TclError, tcl.exprboolean, 'spam')
393        check('', False)
394        for value in ('0', 'false', 'no', 'off'):
395            check(value, False)
396            check('"%s"' % value, False)
397            check('{%s}' % value, False)
398        for value in ('1', 'true', 'yes', 'on'):
399            check(value, True)
400            check('"%s"' % value, True)
401            check('{%s}' % value, True)
402        check('8.2 + 6', True)
403        check('3.1 + $a', True)
404        check('2 + "$a.$b"', True)
405        check('4*[llength "6 2"]', True)
406        check('{word one} < "word $a"', False)
407        check('4*2 < 7', False)
408        check('hypot($a, 4)', True)
409        check('5 / 4', True)
410        check('5 / 4.0', True)
411        check('5 / ( [string length "abcd"] + 0.0 )', True)
412        check('20.0/5.0', True)
413        check('"0x03" > "2"', True)
414        check('[string length "a\xbd\u20ac"]', True)
415        check(r'[string length "a\xbd\u20ac"]', True)
416        self.assertRaises(TclError, tcl.exprboolean, '"abc"')
417        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
418            check('2**64', True)
419
420    @unittest.skipUnless(tcl_version >= (8, 5), 'requires Tcl version >= 8.5')
421    def test_booleans(self):
422        tcl = self.interp
423        def check(expr, expected):
424            result = tcl.call('expr', expr)
425            if tcl.wantobjects():
426                self.assertEqual(result, expected)
427                self.assertIsInstance(result, int)
428            else:
429                self.assertIn(result, (expr, str(int(expected))))
430                self.assertIsInstance(result, str)
431        check('true', True)
432        check('yes', True)
433        check('on', True)
434        check('false', False)
435        check('no', False)
436        check('off', False)
437        check('1 < 2', True)
438        check('1 > 2', False)
439
440    def test_expr_bignum(self):
441        tcl = self.interp
442        for i in self.get_integers():
443            result = tcl.call('expr', str(i))
444            if self.wantobjects:
445                self.assertEqual(result, i)
446                self.assertIsInstance(result, int)
447            else:
448                self.assertEqual(result, str(i))
449                self.assertIsInstance(result, str)
450        if get_tk_patchlevel() < (8, 5):  # bignum was added in Tcl 8.5
451            self.assertRaises(TclError, tcl.call, 'expr', str(2**1000))
452
453    def test_passing_values(self):
454        def passValue(value):
455            return self.interp.call('set', '_', value)
456
457        self.assertEqual(passValue(True), True if self.wantobjects else '1')
458        self.assertEqual(passValue(False), False if self.wantobjects else '0')
459        self.assertEqual(passValue('string'), 'string')
460        self.assertEqual(passValue('string\u20ac'), 'string\u20ac')
461        self.assertEqual(passValue('string\U0001f4bb'), 'string\U0001f4bb')
462        self.assertEqual(passValue('str\x00ing'), 'str\x00ing')
463        self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd')
464        self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac')
465        self.assertEqual(passValue('str\x00ing\U0001f4bb'),
466                         'str\x00ing\U0001f4bb')
467        if sys.platform != 'win32':
468            self.assertEqual(passValue('<\udce2\udc82\udcac>'),
469                             '<\u20ac>')
470            self.assertEqual(passValue('<\udced\udca0\udcbd\udced\udcb2\udcbb>'),
471                             '<\U0001f4bb>')
472        self.assertEqual(passValue(b'str\x00ing'),
473                         b'str\x00ing' if self.wantobjects else 'str\x00ing')
474        self.assertEqual(passValue(b'str\xc0\x80ing'),
475                         b'str\xc0\x80ing' if self.wantobjects else 'str\xc0\x80ing')
476        self.assertEqual(passValue(b'str\xbding'),
477                         b'str\xbding' if self.wantobjects else 'str\xbding')
478        for i in self.get_integers():
479            self.assertEqual(passValue(i), i if self.wantobjects else str(i))
480        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
481            self.assertEqual(passValue(2**1000), str(2**1000))
482        for f in (0.0, 1.0, -1.0, 1/3,
483                  sys.float_info.min, sys.float_info.max,
484                  -sys.float_info.min, -sys.float_info.max):
485            if self.wantobjects:
486                self.assertEqual(passValue(f), f)
487            else:
488                self.assertEqual(float(passValue(f)), f)
489        if self.wantobjects:
490            f = passValue(float('nan'))
491            self.assertNotEqual(f, f)
492            self.assertEqual(passValue(float('inf')), float('inf'))
493            self.assertEqual(passValue(-float('inf')), -float('inf'))
494        else:
495            self.assertEqual(float(passValue(float('inf'))), float('inf'))
496            self.assertEqual(float(passValue(-float('inf'))), -float('inf'))
497            # XXX NaN representation can be not parsable by float()
498        self.assertEqual(passValue((1, '2', (3.4,))),
499                         (1, '2', (3.4,)) if self.wantobjects else '1 2 3.4')
500        self.assertEqual(passValue(['a', ['b', 'c']]),
501                         ('a', ('b', 'c')) if self.wantobjects else 'a {b c}')
502
503    def test_user_command(self):
504        result = None
505        def testfunc(arg):
506            nonlocal result
507            result = arg
508            return arg
509        self.interp.createcommand('testfunc', testfunc)
510        self.addCleanup(self.interp.tk.deletecommand, 'testfunc')
511        def check(value, expected=None, *, eq=self.assertEqual):
512            if expected is None:
513                expected = value
514            nonlocal result
515            result = None
516            r = self.interp.call('testfunc', value)
517            self.assertIsInstance(result, str)
518            eq(result, expected)
519            self.assertIsInstance(r, str)
520            eq(r, expected)
521        def float_eq(actual, expected):
522            self.assertAlmostEqual(float(actual), expected,
523                                   delta=abs(expected) * 1e-10)
524
525        check(True, '1')
526        check(False, '0')
527        check('string')
528        check('string\xbd')
529        check('string\u20ac')
530        check('string\U0001f4bb')
531        if sys.platform != 'win32':
532            check('<\udce2\udc82\udcac>', '<\u20ac>')
533            check('<\udced\udca0\udcbd\udced\udcb2\udcbb>', '<\U0001f4bb>')
534        check('')
535        check(b'string', 'string')
536        check(b'string\xe2\x82\xac', 'string\xe2\x82\xac')
537        check(b'string\xbd', 'string\xbd')
538        check(b'', '')
539        check('str\x00ing')
540        check('str\x00ing\xbd')
541        check('str\x00ing\u20ac')
542        check(b'str\x00ing', 'str\x00ing')
543        check(b'str\xc0\x80ing', 'str\xc0\x80ing')
544        check(b'str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac')
545        for i in self.get_integers():
546            check(i, str(i))
547        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
548            check(2**1000, str(2**1000))
549        for f in (0.0, 1.0, -1.0):
550            check(f, repr(f))
551        for f in (1/3.0, sys.float_info.min, sys.float_info.max,
552                  -sys.float_info.min, -sys.float_info.max):
553            check(f, eq=float_eq)
554        check(float('inf'), eq=float_eq)
555        check(-float('inf'), eq=float_eq)
556        # XXX NaN representation can be not parsable by float()
557        check((), '')
558        check((1, (2,), (3, 4), '5 6', ()), '1 2 {3 4} {5 6} {}')
559        check([1, [2,], [3, 4], '5 6', []], '1 2 {3 4} {5 6} {}')
560
561    def test_splitlist(self):
562        splitlist = self.interp.tk.splitlist
563        call = self.interp.tk.call
564        self.assertRaises(TypeError, splitlist)
565        self.assertRaises(TypeError, splitlist, 'a', 'b')
566        self.assertRaises(TypeError, splitlist, 2)
567        testcases = [
568            ('2', ('2',)),
569            ('', ()),
570            ('{}', ('',)),
571            ('""', ('',)),
572            ('a\n b\t\r c\n ', ('a', 'b', 'c')),
573            (b'a\n b\t\r c\n ', ('a', 'b', 'c')),
574            ('a \u20ac', ('a', '\u20ac')),
575            ('a \U0001f4bb', ('a', '\U0001f4bb')),
576            (b'a \xe2\x82\xac', ('a', '\u20ac')),
577            (b'a \xf0\x9f\x92\xbb', ('a', '\U0001f4bb')),
578            (b'a \xed\xa0\xbd\xed\xb2\xbb', ('a', '\U0001f4bb')),
579            (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
580            ('a {b c}', ('a', 'b c')),
581            (r'a b\ c', ('a', 'b c')),
582            (('a', 'b c'), ('a', 'b c')),
583            ('a 2', ('a', '2')),
584            (('a', 2), ('a', 2)),
585            ('a 3.4', ('a', '3.4')),
586            (('a', 3.4), ('a', 3.4)),
587            ((), ()),
588            ([], ()),
589            (['a', ['b', 'c']], ('a', ['b', 'c'])),
590            (call('list', 1, '2', (3.4,)),
591                (1, '2', (3.4,)) if self.wantobjects else
592                ('1', '2', '3.4')),
593        ]
594        tk_patchlevel = get_tk_patchlevel()
595        if tcl_version >= (8, 5):
596            if not self.wantobjects or tk_patchlevel < (8, 5, 5):
597                # Before 8.5.5 dicts were converted to lists through string
598                expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4')
599            else:
600                expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,))
601            testcases += [
602                (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)),
603                    expected),
604            ]
605        dbg_info = ('want objects? %s, Tcl version: %s, Tk patchlevel: %s'
606                    % (self.wantobjects, tcl_version, tk_patchlevel))
607        for arg, res in testcases:
608            self.assertEqual(splitlist(arg), res,
609                             'arg=%a, %s' % (arg, dbg_info))
610        self.assertRaises(TclError, splitlist, '{')
611
612    def test_split(self):
613        split = self.interp.tk.split
614        call = self.interp.tk.call
615        with warnings.catch_warnings():
616            warnings.filterwarnings('ignore', r'\bsplit\b.*\bsplitlist\b',
617                                    DeprecationWarning)
618            self.assertRaises(TypeError, split)
619            self.assertRaises(TypeError, split, 'a', 'b')
620            self.assertRaises(TypeError, split, 2)
621        testcases = [
622            ('2', '2'),
623            ('', ''),
624            ('{}', ''),
625            ('""', ''),
626            ('{', '{'),
627            ('a\n b\t\r c\n ', ('a', 'b', 'c')),
628            (b'a\n b\t\r c\n ', ('a', 'b', 'c')),
629            ('a \u20ac', ('a', '\u20ac')),
630            (b'a \xe2\x82\xac', ('a', '\u20ac')),
631            (b'a\xc0\x80b', 'a\x00b'),
632            (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
633            (b'{a\xc0\x80b c\xc0\x80d', '{a\x00b c\x00d'),
634            ('a {b c}', ('a', ('b', 'c'))),
635            (r'a b\ c', ('a', ('b', 'c'))),
636            (('a', b'b c'), ('a', ('b', 'c'))),
637            (('a', 'b c'), ('a', ('b', 'c'))),
638            ('a 2', ('a', '2')),
639            (('a', 2), ('a', 2)),
640            ('a 3.4', ('a', '3.4')),
641            (('a', 3.4), ('a', 3.4)),
642            (('a', (2, 3.4)), ('a', (2, 3.4))),
643            ((), ()),
644            ([], ()),
645            (['a', 'b c'], ('a', ('b', 'c'))),
646            (['a', ['b', 'c']], ('a', ('b', 'c'))),
647            (call('list', 1, '2', (3.4,)),
648                (1, '2', (3.4,)) if self.wantobjects else
649                ('1', '2', '3.4')),
650        ]
651        if tcl_version >= (8, 5):
652            if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5):
653                # Before 8.5.5 dicts were converted to lists through string
654                expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4')
655            else:
656                expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,))
657            testcases += [
658                (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)),
659                    expected),
660            ]
661        for arg, res in testcases:
662            with self.assertWarns(DeprecationWarning):
663                self.assertEqual(split(arg), res, msg=arg)
664
665    def test_splitdict(self):
666        splitdict = tkinter._splitdict
667        tcl = self.interp.tk
668
669        arg = '-a {1 2 3} -something foo status {}'
670        self.assertEqual(splitdict(tcl, arg, False),
671            {'-a': '1 2 3', '-something': 'foo', 'status': ''})
672        self.assertEqual(splitdict(tcl, arg),
673            {'a': '1 2 3', 'something': 'foo', 'status': ''})
674
675        arg = ('-a', (1, 2, 3), '-something', 'foo', 'status', '{}')
676        self.assertEqual(splitdict(tcl, arg, False),
677            {'-a': (1, 2, 3), '-something': 'foo', 'status': '{}'})
678        self.assertEqual(splitdict(tcl, arg),
679            {'a': (1, 2, 3), 'something': 'foo', 'status': '{}'})
680
681        self.assertRaises(RuntimeError, splitdict, tcl, '-a b -c ')
682        self.assertRaises(RuntimeError, splitdict, tcl, ('-a', 'b', '-c'))
683
684        arg = tcl.call('list',
685                        '-a', (1, 2, 3), '-something', 'foo', 'status', ())
686        self.assertEqual(splitdict(tcl, arg),
687            {'a': (1, 2, 3) if self.wantobjects else '1 2 3',
688             'something': 'foo', 'status': ''})
689
690        if tcl_version >= (8, 5):
691            arg = tcl.call('dict', 'create',
692                           '-a', (1, 2, 3), '-something', 'foo', 'status', ())
693            if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5):
694                # Before 8.5.5 dicts were converted to lists through string
695                expected = {'a': '1 2 3', 'something': 'foo', 'status': ''}
696            else:
697                expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''}
698            self.assertEqual(splitdict(tcl, arg), expected)
699
700    def test_join(self):
701        join = tkinter._join
702        tcl = self.interp.tk
703        def unpack(s):
704            return tcl.call('lindex', s, 0)
705        def check(value):
706            self.assertEqual(unpack(join([value])), value)
707            self.assertEqual(unpack(join([value, 0])), value)
708            self.assertEqual(unpack(unpack(join([[value]]))), value)
709            self.assertEqual(unpack(unpack(join([[value, 0]]))), value)
710            self.assertEqual(unpack(unpack(join([[value], 0]))), value)
711            self.assertEqual(unpack(unpack(join([[value, 0], 0]))), value)
712        check('')
713        check('spam')
714        check('sp am')
715        check('sp\tam')
716        check('sp\nam')
717        check(' \t\n')
718        check('{spam}')
719        check('{sp am}')
720        check('"spam"')
721        check('"sp am"')
722        check('{"spam"}')
723        check('"{spam}"')
724        check('sp\\am')
725        check('"sp\\am"')
726        check('"{}" "{}"')
727        check('"\\')
728        check('"{')
729        check('"}')
730        check('\n\\')
731        check('\n{')
732        check('\n}')
733        check('\\\n')
734        check('{\n')
735        check('}\n')
736
737    def test_new_tcl_obj(self):
738        self.assertRaises(TypeError, _tkinter.Tcl_Obj)
739
740class BigmemTclTest(unittest.TestCase):
741
742    def setUp(self):
743        self.interp = Tcl()
744
745    @support.cpython_only
746    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
747    @support.bigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False)
748    def test_huge_string_call(self, size):
749        value = ' ' * size
750        self.assertRaises(OverflowError, self.interp.call, 'string', 'index', value, 0)
751
752    @support.cpython_only
753    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
754    @support.bigmemtest(size=INT_MAX + 1, memuse=2, dry_run=False)
755    def test_huge_string_builtins(self, size):
756        tk = self.interp.tk
757        value = '1' + ' ' * size
758        self.assertRaises(OverflowError, tk.getint, value)
759        self.assertRaises(OverflowError, tk.getdouble, value)
760        self.assertRaises(OverflowError, tk.getboolean, value)
761        self.assertRaises(OverflowError, tk.eval, value)
762        self.assertRaises(OverflowError, tk.evalfile, value)
763        self.assertRaises(OverflowError, tk.record, value)
764        self.assertRaises(OverflowError, tk.adderrorinfo, value)
765        self.assertRaises(OverflowError, tk.setvar, value, 'x', 'a')
766        self.assertRaises(OverflowError, tk.setvar, 'x', value, 'a')
767        self.assertRaises(OverflowError, tk.unsetvar, value)
768        self.assertRaises(OverflowError, tk.unsetvar, 'x', value)
769        self.assertRaises(OverflowError, tk.adderrorinfo, value)
770        self.assertRaises(OverflowError, tk.exprstring, value)
771        self.assertRaises(OverflowError, tk.exprlong, value)
772        self.assertRaises(OverflowError, tk.exprboolean, value)
773        self.assertRaises(OverflowError, tk.splitlist, value)
774        self.assertRaises(OverflowError, tk.split, value)
775        self.assertRaises(OverflowError, tk.createcommand, value, max)
776        self.assertRaises(OverflowError, tk.deletecommand, value)
777
778    @support.cpython_only
779    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
780    @support.bigmemtest(size=INT_MAX + 1, memuse=6, dry_run=False)
781    def test_huge_string_builtins2(self, size):
782        # These commands require larger memory for possible error messages
783        tk = self.interp.tk
784        value = '1' + ' ' * size
785        self.assertRaises(OverflowError, tk.evalfile, value)
786        self.assertRaises(OverflowError, tk.unsetvar, value)
787        self.assertRaises(OverflowError, tk.unsetvar, 'x', value)
788
789
790def setUpModule():
791    if support.verbose:
792        tcl = Tcl()
793        print('patchlevel =', tcl.call('info', 'patchlevel'))
794
795
796def test_main():
797    support.run_unittest(TclTest, TkinterTest, BigmemTclTest)
798
799if __name__ == "__main__":
800    test_main()
801