test_turtle.py 12 KB

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