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