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