1import unittest
2import gc
3import tkinter
4from test.support import ALWAYS_EQ
5from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl,
6                     TclError)
7from tkinter.test.support import AbstractDefaultRootTest
8
9
10class Var(Variable):
11
12    _default = "default"
13    side_effect = False
14
15    def set(self, value):
16        self.side_effect = True
17        super().set(value)
18
19
20class TestBase(unittest.TestCase):
21
22    def setUp(self):
23        self.root = Tcl()
24
25    def tearDown(self):
26        del self.root
27
28
29class TestVariable(TestBase):
30
31    def info_exists(self, *args):
32        return self.root.getboolean(self.root.call("info", "exists", *args))
33
34    def test_default(self):
35        v = Variable(self.root)
36        self.assertEqual("", v.get())
37        self.assertRegex(str(v), r"^PY_VAR(\d+)$")
38
39    def test_name_and_value(self):
40        v = Variable(self.root, "sample string", "varname")
41        self.assertEqual("sample string", v.get())
42        self.assertEqual("varname", str(v))
43
44    def test___del__(self):
45        self.assertFalse(self.info_exists("varname"))
46        v = Variable(self.root, "sample string", "varname")
47        self.assertTrue(self.info_exists("varname"))
48        del v
49        self.assertFalse(self.info_exists("varname"))
50
51    def test_dont_unset_not_existing(self):
52        self.assertFalse(self.info_exists("varname"))
53        v1 = Variable(self.root, name="name")
54        v2 = Variable(self.root, name="name")
55        del v1
56        self.assertFalse(self.info_exists("name"))
57        # shouldn't raise exception
58        del v2
59        self.assertFalse(self.info_exists("name"))
60
61    def test_equality(self):
62        # values doesn't matter, only class and name are checked
63        v1 = Variable(self.root, name="abc")
64        v2 = Variable(self.root, name="abc")
65        self.assertEqual(v1, v2)
66
67        v3 = Variable(self.root, name="cba")
68        self.assertNotEqual(v1, v3)
69
70        v4 = StringVar(self.root, name="abc")
71        self.assertEqual(str(v1), str(v4))
72        self.assertNotEqual(v1, v4)
73
74        V = type('Variable', (), {})
75        self.assertNotEqual(v1, V())
76
77        self.assertNotEqual(v1, object())
78        self.assertEqual(v1, ALWAYS_EQ)
79
80        root2 = tkinter.Tk()
81        self.addCleanup(root2.destroy)
82        v5 = Variable(root2, name="abc")
83        self.assertEqual(str(v1), str(v5))
84        self.assertNotEqual(v1, v5)
85
86    def test_invalid_name(self):
87        with self.assertRaises(TypeError):
88            Variable(self.root, name=123)
89
90    def test_null_in_name(self):
91        with self.assertRaises(ValueError):
92            Variable(self.root, name='var\x00name')
93        with self.assertRaises(ValueError):
94            self.root.globalsetvar('var\x00name', "value")
95        with self.assertRaises(ValueError):
96            self.root.globalsetvar(b'var\x00name', "value")
97        with self.assertRaises(ValueError):
98            self.root.setvar('var\x00name', "value")
99        with self.assertRaises(ValueError):
100            self.root.setvar(b'var\x00name', "value")
101
102    def test_initialize(self):
103        v = Var(self.root)
104        self.assertFalse(v.side_effect)
105        v.set("value")
106        self.assertTrue(v.side_effect)
107
108    def test_trace_old(self):
109        # Old interface
110        v = Variable(self.root)
111        vname = str(v)
112        trace = []
113        def read_tracer(*args):
114            trace.append(('read',) + args)
115        def write_tracer(*args):
116            trace.append(('write',) + args)
117        cb1 = v.trace_variable('r', read_tracer)
118        cb2 = v.trace_variable('wu', write_tracer)
119        self.assertEqual(sorted(v.trace_vinfo()), [('r', cb1), ('wu', cb2)])
120        self.assertEqual(trace, [])
121
122        v.set('spam')
123        self.assertEqual(trace, [('write', vname, '', 'w')])
124
125        trace = []
126        v.get()
127        self.assertEqual(trace, [('read', vname, '', 'r')])
128
129        trace = []
130        info = sorted(v.trace_vinfo())
131        v.trace_vdelete('w', cb1)  # Wrong mode
132        self.assertEqual(sorted(v.trace_vinfo()), info)
133        with self.assertRaises(TclError):
134            v.trace_vdelete('r', 'spam')  # Wrong command name
135        self.assertEqual(sorted(v.trace_vinfo()), info)
136        v.trace_vdelete('r', (cb1, 43)) # Wrong arguments
137        self.assertEqual(sorted(v.trace_vinfo()), info)
138        v.get()
139        self.assertEqual(trace, [('read', vname, '', 'r')])
140
141        trace = []
142        v.trace_vdelete('r', cb1)
143        self.assertEqual(v.trace_vinfo(), [('wu', cb2)])
144        v.get()
145        self.assertEqual(trace, [])
146
147        trace = []
148        del write_tracer
149        gc.collect()
150        v.set('eggs')
151        self.assertEqual(trace, [('write', vname, '', 'w')])
152
153        trace = []
154        del v
155        gc.collect()
156        self.assertEqual(trace, [('write', vname, '', 'u')])
157
158    def test_trace(self):
159        v = Variable(self.root)
160        vname = str(v)
161        trace = []
162        def read_tracer(*args):
163            trace.append(('read',) + args)
164        def write_tracer(*args):
165            trace.append(('write',) + args)
166        tr1 = v.trace_add('read', read_tracer)
167        tr2 = v.trace_add(['write', 'unset'], write_tracer)
168        self.assertEqual(sorted(v.trace_info()), [
169                         (('read',), tr1),
170                         (('write', 'unset'), tr2)])
171        self.assertEqual(trace, [])
172
173        v.set('spam')
174        self.assertEqual(trace, [('write', vname, '', 'write')])
175
176        trace = []
177        v.get()
178        self.assertEqual(trace, [('read', vname, '', 'read')])
179
180        trace = []
181        info = sorted(v.trace_info())
182        v.trace_remove('write', tr1)  # Wrong mode
183        self.assertEqual(sorted(v.trace_info()), info)
184        with self.assertRaises(TclError):
185            v.trace_remove('read', 'spam')  # Wrong command name
186        self.assertEqual(sorted(v.trace_info()), info)
187        v.get()
188        self.assertEqual(trace, [('read', vname, '', 'read')])
189
190        trace = []
191        v.trace_remove('read', tr1)
192        self.assertEqual(v.trace_info(), [(('write', 'unset'), tr2)])
193        v.get()
194        self.assertEqual(trace, [])
195
196        trace = []
197        del write_tracer
198        gc.collect()
199        v.set('eggs')
200        self.assertEqual(trace, [('write', vname, '', 'write')])
201
202        trace = []
203        del v
204        gc.collect()
205        self.assertEqual(trace, [('write', vname, '', 'unset')])
206
207
208class TestStringVar(TestBase):
209
210    def test_default(self):
211        v = StringVar(self.root)
212        self.assertEqual("", v.get())
213
214    def test_get(self):
215        v = StringVar(self.root, "abc", "name")
216        self.assertEqual("abc", v.get())
217        self.root.globalsetvar("name", "value")
218        self.assertEqual("value", v.get())
219
220    def test_get_null(self):
221        v = StringVar(self.root, "abc\x00def", "name")
222        self.assertEqual("abc\x00def", v.get())
223        self.root.globalsetvar("name", "val\x00ue")
224        self.assertEqual("val\x00ue", v.get())
225
226
227class TestIntVar(TestBase):
228
229    def test_default(self):
230        v = IntVar(self.root)
231        self.assertEqual(0, v.get())
232
233    def test_get(self):
234        v = IntVar(self.root, 123, "name")
235        self.assertEqual(123, v.get())
236        self.root.globalsetvar("name", "345")
237        self.assertEqual(345, v.get())
238        self.root.globalsetvar("name", "876.5")
239        self.assertEqual(876, v.get())
240
241    def test_invalid_value(self):
242        v = IntVar(self.root, name="name")
243        self.root.globalsetvar("name", "value")
244        with self.assertRaises((ValueError, TclError)):
245            v.get()
246
247
248class TestDoubleVar(TestBase):
249
250    def test_default(self):
251        v = DoubleVar(self.root)
252        self.assertEqual(0.0, v.get())
253
254    def test_get(self):
255        v = DoubleVar(self.root, 1.23, "name")
256        self.assertAlmostEqual(1.23, v.get())
257        self.root.globalsetvar("name", "3.45")
258        self.assertAlmostEqual(3.45, v.get())
259
260    def test_get_from_int(self):
261        v = DoubleVar(self.root, 1.23, "name")
262        self.assertAlmostEqual(1.23, v.get())
263        self.root.globalsetvar("name", "3.45")
264        self.assertAlmostEqual(3.45, v.get())
265        self.root.globalsetvar("name", "456")
266        self.assertAlmostEqual(456, v.get())
267
268    def test_invalid_value(self):
269        v = DoubleVar(self.root, name="name")
270        self.root.globalsetvar("name", "value")
271        with self.assertRaises((ValueError, TclError)):
272            v.get()
273
274
275class TestBooleanVar(TestBase):
276
277    def test_default(self):
278        v = BooleanVar(self.root)
279        self.assertIs(v.get(), False)
280
281    def test_get(self):
282        v = BooleanVar(self.root, True, "name")
283        self.assertIs(v.get(), True)
284        self.root.globalsetvar("name", "0")
285        self.assertIs(v.get(), False)
286        self.root.globalsetvar("name", 42 if self.root.wantobjects() else 1)
287        self.assertIs(v.get(), True)
288        self.root.globalsetvar("name", 0)
289        self.assertIs(v.get(), False)
290        self.root.globalsetvar("name", "on")
291        self.assertIs(v.get(), True)
292
293    def test_set(self):
294        true = 1 if self.root.wantobjects() else "1"
295        false = 0 if self.root.wantobjects() else "0"
296        v = BooleanVar(self.root, name="name")
297        v.set(True)
298        self.assertEqual(self.root.globalgetvar("name"), true)
299        v.set("0")
300        self.assertEqual(self.root.globalgetvar("name"), false)
301        v.set(42)
302        self.assertEqual(self.root.globalgetvar("name"), true)
303        v.set(0)
304        self.assertEqual(self.root.globalgetvar("name"), false)
305        v.set("on")
306        self.assertEqual(self.root.globalgetvar("name"), true)
307
308    def test_invalid_value_domain(self):
309        false = 0 if self.root.wantobjects() else "0"
310        v = BooleanVar(self.root, name="name")
311        with self.assertRaises(TclError):
312            v.set("value")
313        self.assertEqual(self.root.globalgetvar("name"), false)
314        self.root.globalsetvar("name", "value")
315        with self.assertRaises(ValueError):
316            v.get()
317        self.root.globalsetvar("name", "1.0")
318        with self.assertRaises(ValueError):
319            v.get()
320
321
322class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
323
324    def test_variable(self):
325        self.assertRaises(RuntimeError, Variable)
326        root = tkinter.Tk()
327        v = Variable()
328        v.set("value")
329        self.assertEqual(v.get(), "value")
330        root.destroy()
331        tkinter.NoDefaultRoot()
332        self.assertRaises(RuntimeError, Variable)
333
334
335tests_gui = (TestVariable, TestStringVar, TestIntVar,
336             TestDoubleVar, TestBooleanVar, DefaultRootTest)
337
338
339if __name__ == "__main__":
340    from test.support import run_unittest
341    run_unittest(*tests_gui)
342