blob: 46ff4a3aac709a721537f8fb24e861ac0da3fcfc [file] [log] [blame]
Serhiy Storchaka3c5fa562016-06-05 10:32:57 +03001import pickle
Serhiy Storchaka41e9ad12016-06-04 23:27:26 +03002import unittest
3from test import support
Hai Shi96a6a6d2020-07-09 21:25:10 +08004from test.support import import_helper
Hai Shi79bb2c92020-08-06 19:51:29 +08005from test.support import os_helper
Serhiy Storchaka41e9ad12016-06-04 23:27:26 +03006
Hai Shi96a6a6d2020-07-09 21:25:10 +08007
8turtle = import_helper.import_module('turtle')
Serhiy Storchaka41e9ad12016-06-04 23:27:26 +03009Vec2D = turtle.Vec2D
10
11test_config = """\
12width = 0.75
13height = 0.8
14canvwidth = 500
15canvheight = 200
16leftright = 100
17topbottom = 100
18mode = world
19colormode = 255
20delay = 100
21undobuffersize = 10000
22shape = circle
23pencolor = red
24fillcolor = blue
25resizemode = auto
26visible = None
27language = english
28exampleturtle = turtle
29examplescreen = screen
30title = Python Turtle Graphics
31using_IDLE = ''
32"""
33
34test_config_two = """\
35# Comments!
36# Testing comments!
37pencolor = red
38fillcolor = blue
39visible = False
40language = english
41# Some more
42# comments
43using_IDLE = False
44"""
45
46invalid_test_config = """
47pencolor = red
48fillcolor: blue
49visible = False
50"""
51
52
53class TurtleConfigTest(unittest.TestCase):
54
55 def get_cfg_file(self, cfg_str):
Hai Shi79bb2c92020-08-06 19:51:29 +080056 self.addCleanup(os_helper.unlink, os_helper.TESTFN)
57 with open(os_helper.TESTFN, 'w') as f:
Serhiy Storchaka41e9ad12016-06-04 23:27:26 +030058 f.write(cfg_str)
Hai Shi79bb2c92020-08-06 19:51:29 +080059 return os_helper.TESTFN
Serhiy Storchaka41e9ad12016-06-04 23:27:26 +030060
61 def test_config_dict(self):
62
63 cfg_name = self.get_cfg_file(test_config)
64 parsed_cfg = turtle.config_dict(cfg_name)
65
66 expected = {
67 'width' : 0.75,
68 'height' : 0.8,
69 'canvwidth' : 500,
70 'canvheight': 200,
71 'leftright': 100,
72 'topbottom': 100,
73 'mode': 'world',
74 'colormode': 255,
75 'delay': 100,
76 'undobuffersize': 10000,
77 'shape': 'circle',
78 'pencolor' : 'red',
79 'fillcolor' : 'blue',
80 'resizemode' : 'auto',
81 'visible' : None,
82 'language': 'english',
83 'exampleturtle': 'turtle',
84 'examplescreen': 'screen',
85 'title': 'Python Turtle Graphics',
86 'using_IDLE': '',
87 }
88
89 self.assertEqual(parsed_cfg, expected)
90
Min ho Kimc4cacc82019-07-31 08:16:13 +100091 def test_partial_config_dict_with_comments(self):
Serhiy Storchaka41e9ad12016-06-04 23:27:26 +030092
93 cfg_name = self.get_cfg_file(test_config_two)
94 parsed_cfg = turtle.config_dict(cfg_name)
95
96 expected = {
97 'pencolor': 'red',
98 'fillcolor': 'blue',
99 'visible': False,
100 'language': 'english',
101 'using_IDLE': False,
102 }
103
104 self.assertEqual(parsed_cfg, expected)
105
106 def test_config_dict_invalid(self):
107
108 cfg_name = self.get_cfg_file(invalid_test_config)
109
110 with support.captured_stdout() as stdout:
111 parsed_cfg = turtle.config_dict(cfg_name)
112
113 err_msg = stdout.getvalue()
114
115 self.assertIn('Bad line in config-file ', err_msg)
116 self.assertIn('fillcolor: blue', err_msg)
117
118 self.assertEqual(parsed_cfg, {
119 'pencolor': 'red',
120 'visible': False,
121 })
122
123
124class VectorComparisonMixin:
125
126 def assertVectorsAlmostEqual(self, vec1, vec2):
127 if len(vec1) != len(vec2):
128 self.fail("Tuples are not of equal size")
129 for idx, (i, j) in enumerate(zip(vec1, vec2)):
130 self.assertAlmostEqual(
131 i, j, msg='values at index {} do not match'.format(idx))
132
133
134class TestVec2D(VectorComparisonMixin, unittest.TestCase):
135
Serhiy Storchaka3c5fa562016-06-05 10:32:57 +0300136 def test_constructor(self):
137 vec = Vec2D(0.5, 2)
138 self.assertEqual(vec[0], 0.5)
139 self.assertEqual(vec[1], 2)
140 self.assertIsInstance(vec, Vec2D)
141
142 self.assertRaises(TypeError, Vec2D)
143 self.assertRaises(TypeError, Vec2D, 0)
144 self.assertRaises(TypeError, Vec2D, (0, 1))
145 self.assertRaises(TypeError, Vec2D, vec)
146 self.assertRaises(TypeError, Vec2D, 0, 1, 2)
147
148 def test_repr(self):
149 vec = Vec2D(0.567, 1.234)
150 self.assertEqual(repr(vec), '(0.57,1.23)')
151
152 def test_equality(self):
153 vec1 = Vec2D(0, 1)
154 vec2 = Vec2D(0.0, 1)
155 vec3 = Vec2D(42, 1)
156 self.assertEqual(vec1, vec2)
157 self.assertEqual(vec1, tuple(vec1))
158 self.assertEqual(tuple(vec1), vec1)
159 self.assertNotEqual(vec1, vec3)
160 self.assertNotEqual(vec2, vec3)
161
162 def test_pickling(self):
163 vec = Vec2D(0.5, 2)
164 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
165 with self.subTest(proto=proto):
166 pickled = pickle.dumps(vec, protocol=proto)
167 unpickled = pickle.loads(pickled)
168 self.assertEqual(unpickled, vec)
169 self.assertIsInstance(unpickled, Vec2D)
170
Serhiy Storchaka41e9ad12016-06-04 23:27:26 +0300171 def _assert_arithmetic_cases(self, test_cases, lambda_operator):
172 for test_case in test_cases:
173 with self.subTest(case=test_case):
174
175 ((first, second), expected) = test_case
176
177 op1 = Vec2D(*first)
178 op2 = Vec2D(*second)
179
180 result = lambda_operator(op1, op2)
181
182 expected = Vec2D(*expected)
183
184 self.assertVectorsAlmostEqual(result, expected)
185
186 def test_vector_addition(self):
187
188 test_cases = [
189 (((0, 0), (1, 1)), (1.0, 1.0)),
190 (((-1, 0), (2, 2)), (1, 2)),
191 (((1.5, 0), (1, 1)), (2.5, 1)),
192 ]
193
194 self._assert_arithmetic_cases(test_cases, lambda x, y: x + y)
195
196 def test_vector_subtraction(self):
197
198 test_cases = [
199 (((0, 0), (1, 1)), (-1, -1)),
200 (((10.625, 0.125), (10, 0)), (0.625, 0.125)),
201 ]
202
203 self._assert_arithmetic_cases(test_cases, lambda x, y: x - y)
204
205 def test_vector_multiply(self):
206
207 vec1 = Vec2D(10, 10)
208 vec2 = Vec2D(0.5, 3)
209 answer = vec1 * vec2
210 expected = 35
211 self.assertAlmostEqual(answer, expected)
212
213 vec = Vec2D(0.5, 3)
214 answer = vec * 10
215 expected = Vec2D(5, 30)
216 self.assertVectorsAlmostEqual(answer, expected)
217
218 def test_vector_negative(self):
219 vec = Vec2D(10, -10)
220 expected = (-10, 10)
221 self.assertVectorsAlmostEqual(-vec, expected)
222
223 def test_distance(self):
224 vec = Vec2D(6, 8)
225 expected = 10
226 self.assertEqual(abs(vec), expected)
227
228 vec = Vec2D(0, 0)
229 expected = 0
230 self.assertEqual(abs(vec), expected)
231
232 vec = Vec2D(2.5, 6)
233 expected = 6.5
234 self.assertEqual(abs(vec), expected)
235
236 def test_rotate(self):
237
238 cases = [
239 (((0, 0), 0), (0, 0)),
240 (((0, 1), 90), (-1, 0)),
241 (((0, 1), -90), (1, 0)),
242 (((1, 0), 180), (-1, 0)),
243 (((1, 0), 360), (1, 0)),
244 ]
245
246 for case in cases:
247 with self.subTest(case=case):
248 (vec, rot), expected = case
249 vec = Vec2D(*vec)
250 got = vec.rotate(rot)
251 self.assertVectorsAlmostEqual(got, expected)
252
253
254class TestTNavigator(VectorComparisonMixin, unittest.TestCase):
255
256 def setUp(self):
257 self.nav = turtle.TNavigator()
258
259 def test_goto(self):
260 self.nav.goto(100, -100)
261 self.assertAlmostEqual(self.nav.xcor(), 100)
262 self.assertAlmostEqual(self.nav.ycor(), -100)
263
264 def test_pos(self):
265 self.assertEqual(self.nav.pos(), self.nav._position)
266 self.nav.goto(100, -100)
267 self.assertEqual(self.nav.pos(), self.nav._position)
268
269 def test_left(self):
270 self.assertEqual(self.nav._orient, (1.0, 0))
271 self.nav.left(90)
272 self.assertVectorsAlmostEqual(self.nav._orient, (0.0, 1.0))
273
274 def test_right(self):
275 self.assertEqual(self.nav._orient, (1.0, 0))
276 self.nav.right(90)
277 self.assertVectorsAlmostEqual(self.nav._orient, (0, -1.0))
278
279 def test_reset(self):
280 self.nav.goto(100, -100)
281 self.assertAlmostEqual(self.nav.xcor(), 100)
282 self.assertAlmostEqual(self.nav.ycor(), -100)
283 self.nav.reset()
284 self.assertAlmostEqual(self.nav.xcor(), 0)
285 self.assertAlmostEqual(self.nav.ycor(), 0)
286
287 def test_forward(self):
288 self.nav.forward(150)
289 expected = Vec2D(150, 0)
290 self.assertVectorsAlmostEqual(self.nav.position(), expected)
291
292 self.nav.reset()
293 self.nav.left(90)
294 self.nav.forward(150)
295 expected = Vec2D(0, 150)
296 self.assertVectorsAlmostEqual(self.nav.position(), expected)
297
298 self.assertRaises(TypeError, self.nav.forward, 'skldjfldsk')
299
300 def test_backwards(self):
301 self.nav.back(200)
302 expected = Vec2D(-200, 0)
303 self.assertVectorsAlmostEqual(self.nav.position(), expected)
304
305 self.nav.reset()
306 self.nav.right(90)
307 self.nav.back(200)
308 expected = Vec2D(0, 200)
309 self.assertVectorsAlmostEqual(self.nav.position(), expected)
310
311 def test_distance(self):
312 self.nav.forward(100)
313 expected = 100
314 self.assertAlmostEqual(self.nav.distance(Vec2D(0,0)), expected)
315
316 def test_radians_and_degrees(self):
317 self.nav.left(90)
318 self.assertAlmostEqual(self.nav.heading(), 90)
319 self.nav.radians()
320 self.assertAlmostEqual(self.nav.heading(), 1.57079633)
321 self.nav.degrees()
322 self.assertAlmostEqual(self.nav.heading(), 90)
323
324 def test_towards(self):
325
326 coordinates = [
327 # coordinates, expected
328 ((100, 0), 0.0),
329 ((100, 100), 45.0),
330 ((0, 100), 90.0),
331 ((-100, 100), 135.0),
332 ((-100, 0), 180.0),
333 ((-100, -100), 225.0),
334 ((0, -100), 270.0),
335 ((100, -100), 315.0),
336 ]
337
338 for (x, y), expected in coordinates:
339 self.assertEqual(self.nav.towards(x, y), expected)
340 self.assertEqual(self.nav.towards((x, y)), expected)
341 self.assertEqual(self.nav.towards(Vec2D(x, y)), expected)
342
343 def test_heading(self):
344
345 self.nav.left(90)
346 self.assertAlmostEqual(self.nav.heading(), 90)
347 self.nav.left(45)
348 self.assertAlmostEqual(self.nav.heading(), 135)
349 self.nav.right(1.6)
350 self.assertAlmostEqual(self.nav.heading(), 133.4)
351 self.assertRaises(TypeError, self.nav.right, 'sdkfjdsf')
352 self.nav.reset()
353
354 rotations = [10, 20, 170, 300]
355 result = sum(rotations) % 360
356 for num in rotations:
357 self.nav.left(num)
358 self.assertEqual(self.nav.heading(), result)
359 self.nav.reset()
360
361 result = (360-sum(rotations)) % 360
362 for num in rotations:
363 self.nav.right(num)
364 self.assertEqual(self.nav.heading(), result)
365 self.nav.reset()
366
367 rotations = [10, 20, -170, 300, -210, 34.3, -50.2, -10, -29.98, 500]
368 sum_so_far = 0
369 for num in rotations:
370 if num < 0:
371 self.nav.right(abs(num))
372 else:
373 self.nav.left(num)
374 sum_so_far += num
375 self.assertAlmostEqual(self.nav.heading(), sum_so_far % 360)
376
377 def test_setheading(self):
378 self.nav.setheading(102.32)
379 self.assertAlmostEqual(self.nav.heading(), 102.32)
380 self.nav.setheading(-123.23)
381 self.assertAlmostEqual(self.nav.heading(), (-123.23) % 360)
382 self.nav.setheading(-1000.34)
383 self.assertAlmostEqual(self.nav.heading(), (-1000.34) % 360)
384 self.nav.setheading(300000)
385 self.assertAlmostEqual(self.nav.heading(), 300000%360)
386
387 def test_positions(self):
388 self.nav.forward(100)
389 self.nav.left(90)
390 self.nav.forward(-200)
391 self.assertVectorsAlmostEqual(self.nav.pos(), (100.0, -200.0))
392
393 def test_setx_and_sety(self):
394 self.nav.setx(-1023.2334)
395 self.nav.sety(193323.234)
396 self.assertVectorsAlmostEqual(self.nav.pos(), (-1023.2334, 193323.234))
397
398 def test_home(self):
399 self.nav.left(30)
400 self.nav.forward(-100000)
401 self.nav.home()
402 self.assertVectorsAlmostEqual(self.nav.pos(), (0,0))
403 self.assertAlmostEqual(self.nav.heading(), 0)
404
405 def test_distance_method(self):
406 self.assertAlmostEqual(self.nav.distance(30, 40), 50)
407 vec = Vec2D(0.22, .001)
408 self.assertAlmostEqual(self.nav.distance(vec), 0.22000227271553355)
409 another_turtle = turtle.TNavigator()
410 another_turtle.left(90)
411 another_turtle.forward(10000)
412 self.assertAlmostEqual(self.nav.distance(another_turtle), 10000)
413
414
415class TestTPen(unittest.TestCase):
416
417 def test_pendown_and_penup(self):
418
419 tpen = turtle.TPen()
420
421 self.assertTrue(tpen.isdown())
422 tpen.penup()
423 self.assertFalse(tpen.isdown())
424 tpen.pendown()
425 self.assertTrue(tpen.isdown())
426
427 def test_showturtle_hideturtle_and_isvisible(self):
428
429 tpen = turtle.TPen()
430
431 self.assertTrue(tpen.isvisible())
432 tpen.hideturtle()
433 self.assertFalse(tpen.isvisible())
434 tpen.showturtle()
435 self.assertTrue(tpen.isvisible())
436
437
438if __name__ == '__main__':
439 unittest.main()