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