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