Skip to content

Commit 1081e49

Browse files
committed
added tests
1 parent 0afc0f3 commit 1081e49

3 files changed

Lines changed: 254 additions & 0 deletions

File tree

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
include demo/*.py
2+
include tests/*.py

tests/__init__.py

Whitespace-only changes.

tests/test_tkthread.py

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import unittest
2+
import threading
3+
import time
4+
5+
import tkthread
6+
7+
8+
def run(func, args=(), kwargs=None, name=None):
9+
th = threading.Thread(target=func, args=args, kwargs=kwargs,
10+
name=name)
11+
th.daemon = True
12+
th.start()
13+
return th
14+
15+
16+
def thread_start(*args, **kwargs):
17+
"""Decorator that returns a started thread running the function."""
18+
def decorator(func):
19+
th = run(func, args, kwargs, name='thread_start')
20+
return th
21+
return decorator
22+
23+
24+
def call_until(timeout=None):
25+
"""Decorator that returns if a call has timed out."""
26+
t = time.time()
27+
28+
def decorator(func, timeout=timeout):
29+
while func():
30+
if timeout is not None:
31+
if time.time() - t >= timeout:
32+
return True
33+
return False
34+
return decorator
35+
36+
37+
class ExpectedTestError(Exception):
38+
pass
39+
40+
41+
class TestResultClass(unittest.TestCase):
42+
43+
def test_event(self):
44+
r = tkthread.Result()
45+
self.assertEqual(r.event.is_set(), False)
46+
47+
def test_set_result(self):
48+
r = tkthread.Result()
49+
self.assertEqual(r.result, None)
50+
51+
@thread_start(r)
52+
def tset(r):
53+
r.set(True, is_error=False)
54+
tset.join()
55+
56+
self.assertEqual(r.result, True)
57+
58+
def test_set_error(self):
59+
r = tkthread.Result()
60+
61+
@thread_start(r)
62+
def tset(r):
63+
r.set((RuntimeError, 'testing', None),
64+
is_error=True)
65+
tset.join()
66+
67+
with self.assertRaises(RuntimeError):
68+
r.get()
69+
70+
def test_wait(self):
71+
r = tkthread.Result()
72+
done = []
73+
74+
@thread_start(r)
75+
def twait(r):
76+
result = r.get()
77+
done.append(result)
78+
79+
self.assertEqual(r.event.is_set(), False)
80+
self.assertFalse(done)
81+
82+
r.set(None)
83+
self.assertEqual(r.event.is_set(), True)
84+
85+
twait.join() # wait for thread to finish
86+
self.assertIs(done[0], None)
87+
88+
89+
class TestTkThread(unittest.TestCase):
90+
91+
@classmethod
92+
def setUpClass(cls):
93+
cls.root = tkthread.tk.Tk()
94+
cls.root.withdraw()
95+
96+
@classmethod
97+
def tearDownClass(cls):
98+
cls.root.destroy()
99+
cls.root = None
100+
101+
def setUp(self):
102+
self.had_error = False
103+
self.tkt = tkthread.TkThread(self.root)
104+
105+
def tearDown(self):
106+
self.tkt.destroy()
107+
108+
def test_init_from_thread(self):
109+
110+
@thread_start(self)
111+
def tstart(self):
112+
try:
113+
tkthread.TkThread(self.root)
114+
except RuntimeError:
115+
self.had_error = True
116+
117+
tstart.join(5.0)
118+
self.assertFalse(tstart.is_alive())
119+
self.assertTrue(self.had_error)
120+
121+
def test_call(self):
122+
123+
@thread_start(self)
124+
def tstart(self):
125+
try:
126+
self.tkt(self.root.wm_title, 'SUCCESS')
127+
except RuntimeError:
128+
self.had_error = True
129+
130+
while tstart.is_alive():
131+
self.root.update()
132+
133+
self.assertFalse(self.had_error)
134+
135+
def test_no_wrap(self):
136+
# Sanity check.
137+
# If this test fails, then something has changed with Tkinter
138+
139+
@thread_start(self)
140+
def tstart(self):
141+
try:
142+
self.root.wm_title('FAIL')
143+
except RuntimeError:
144+
self.had_error = True
145+
146+
time.sleep(2) # relies on _tkinter.c behavior
147+
self.assertTrue(self.had_error)
148+
149+
def test_call_error(self):
150+
# This test will cause a traceback to display
151+
152+
@thread_start(self)
153+
def tstart(self):
154+
def errorfunc():
155+
raise ExpectedTestError('expected in Tkinter callback')
156+
try:
157+
self.tkt(errorfunc)
158+
except ExpectedTestError:
159+
self.had_error = True
160+
161+
while tstart.is_alive():
162+
self.root.update() # process the event
163+
164+
self.assertTrue(self.had_error)
165+
166+
def test_install(self):
167+
_orig_tk = self.root.tk
168+
self.tkt.install()
169+
170+
@thread_start(self)
171+
def tstart(self):
172+
try:
173+
self.root.eval('')
174+
except RuntimeError:
175+
self.had_error = True
176+
raise
177+
178+
time.sleep(2) # relies on _tkinter.c behavior
179+
180+
while tstart.is_alive():
181+
self.root.update()
182+
183+
self.assertFalse(self.had_error)
184+
self.root.tk = _orig_tk # "tkt.uninstall"
185+
186+
def test_install_children(self):
187+
_orig_tk = self.root.tk
188+
189+
text = tkthread.tk.Text(self.root)
190+
with self.assertRaises(RuntimeError):
191+
self.tkt.install()
192+
193+
del text
194+
self.root.tk = _orig_tk # in case test failed
195+
196+
def test_call_main(self):
197+
# call from the main thread immediately
198+
with self.assertRaises(tkthread.tk.TclError):
199+
self.tkt(self.root.eval, '-')
200+
self.tkt(self.root.eval, '')
201+
202+
def test_nosync(self):
203+
204+
ev_nosync = threading.Event()
205+
ev = threading.Event()
206+
207+
@thread_start(self, ev_nosync)
208+
def nosync(self, ev_nosync):
209+
self.tkt.nosync(ev_nosync.set)
210+
211+
nosync.join(5.0)
212+
self.assertFalse(nosync.is_alive())
213+
214+
@thread_start(ev)
215+
def wait_thread(ev):
216+
ev.wait()
217+
218+
@thread_start(self, ev)
219+
def set_ev(self, ev):
220+
self.tkt(ev.set)
221+
222+
while wait_thread.is_alive():
223+
self.root.update()
224+
225+
self.assertTrue(ev_nosync.is_set())
226+
227+
def test_destroy(self):
228+
229+
@thread_start(self)
230+
def block(self):
231+
# root.update not called, so .tkt will block
232+
try:
233+
self.tkt(lambda: None)
234+
except RuntimeError:
235+
self.has_error = True
236+
237+
@call_until(4)
238+
def wait_tkt():
239+
# need to wait for the .tkt call to execute
240+
# so that the Result object can be set by .destroy
241+
return not bool(self.tkt._call_from_data)
242+
243+
self.assertFalse(wait_tkt)
244+
245+
self.tkt.destroy()
246+
247+
block.join(5.0)
248+
self.assertFalse(block.is_alive())
249+
self.assertTrue(self.has_error)
250+
251+
252+
if __name__ == '__main__':
253+
unittest.main()

0 commit comments

Comments
 (0)